From 59df3a90f41461562a80688337ec53687e341124 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 15 May 2017 14:17:06 -0700 Subject: [PATCH 01/15] [core] Improved label pitch-scaling: approximate collision box shapes based on tile distance from camera. --- src/mbgl/map/transform_state.cpp | 12 +++++++ src/mbgl/map/transform_state.hpp | 2 ++ src/mbgl/programs/symbol_program.cpp | 2 ++ src/mbgl/programs/symbol_program.hpp | 6 +++- src/mbgl/renderer/tile_pyramid.cpp | 13 +++++--- src/mbgl/text/collision_tile.cpp | 50 +++++++++++++++++++--------- src/mbgl/text/collision_tile.hpp | 6 ++-- src/mbgl/text/placement_config.hpp | 8 +++-- src/mbgl/tile/geometry_tile.cpp | 4 +++ src/mbgl/tile/geometry_tile.hpp | 2 ++ src/mbgl/tile/tile.hpp | 2 ++ 11 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index f052e30a6b2..4606e3eece4 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -385,4 +385,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 e6464aeaccf..f35f5705490 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -86,6 +86,8 @@ class TransformState { 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; diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index cdbd6b9713a..789eed0dd8a 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,7 @@ Values makeValues(const bool isText, uniforms::u_texture::Value{ 0 }, uniforms::u_fadetexture::Value{ 1 }, uniforms::u_is_text::Value{ isText }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, std::forward(args)... }; } diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index c11e0b5ca14..d1a6b4b9942 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -42,6 +42,7 @@ 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_collision_y_stretch); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< @@ -389,7 +390,8 @@ class SymbolIconProgram : public SymbolProgram< uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_is_text>, + uniforms::u_is_text, + uniforms::u_collision_y_stretch>, style::IconPaintProperties> { public: @@ -422,6 +424,7 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_texture, uniforms::u_fadetexture, uniforms::u_is_text, + uniforms::u_collision_y_stretch, uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, @@ -443,6 +446,7 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_texture, uniforms::u_fadetexture, uniforms::u_is_text, + uniforms::u_collision_y_stretch, uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index a8e6c128ba6..57e7f6d375c 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -175,11 +175,16 @@ void TilePyramid::update(const std::vector>& layer removeStaleTiles(retain); - const PlacementConfig config { parameters.transformState.getAngle(), - parameters.transformState.getPitch(), - parameters.debugOptions & MapDebugOptions::Collision }; - for (auto& pair : tiles) { + // TODO: Calculating yStretch based on tile distance means we can no longer use the same collision tile for two wrapped + // copies of the same data tile. For now the assumption that we're always at "wrap = 0" means collision detection is broken + // for wrapped tiles. + const PlacementConfig config { parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.transformState.getCameraToCenterDistance(), + parameters.transformState.getCameraToTileDistance(pair.first.unwrapTo(0)), + parameters.debugOptions & MapDebugOptions::Collision }; + pair.second->setPlacementConfig(config); } } diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index 368750c89f3..520f66ead8d 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -20,12 +20,17 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_ 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); + perspectiveRatio = 1.0f + 0.5f * ((config.cameraToTileDistance / config.cameraToCenterDistance) - 1.0f); + minScale /= perspectiveRatio; + maxScale /= perspectiveRatio; + + // We can only approximate here based on the y position of the tile + // The shaders calculate a more accurate "incidence_stretch" + // at render time to calculate an effective scale for collision + // purposes, but we still want to use the yStretch approximation + // here because we can't adjust the aspect ratio of the collision + // boxes at render time. + yStretch = util::max(1.0f, config.cameraToTileDistance / (config.cameraToCenterDistance * std::cos(config.pitch))); } @@ -157,13 +162,21 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS Box CollisionTile::getTreeBox(const Point& anchor, const CollisionBox& box, const float scale) { assert(box.x1 <= box.x2 && box.y1 <= box.y2); return Box{ + // When the 'perspectiveRatio' is high, we're effectively underzooming + // the tile because it's in the distance. + // In order to detect collisions that only happen while underzoomed, + // we have to query a larger portion of the grid. + // This extra work is offset by having a lower 'maxScale' bound + // Note that this adjustment ONLY affects the bounding boxes + // in the grid. It doesn't affect the boxes used for the + // minPlacementScale calculations. CollisionPoint{ - anchor.x + box.x1 / scale, - anchor.y + box.y1 / scale * yStretch + anchor.x + box.x1 / scale * perspectiveRatio, + anchor.y + box.y1 / scale * yStretch * perspectiveRatio, }, CollisionPoint{ - anchor.x + box.x2 / scale, - anchor.y + box.y2 / scale * yStretch + anchor.x + box.x2 / scale * perspectiveRatio, + anchor.y + box.y2 / scale * yStretch * perspectiveRatio } }; } @@ -190,8 +203,14 @@ std::vector CollisionTile::queryRenderedSymbols(const Geometr return seenFeatures.find(feature.index) == seenFeatures.end(); }; + // "perspectiveRatio" is a tile-based approximation of how much larger symbols will + // be in the distance. It won't line up exactly with the actually rendered symbols + // Being exact would require running the collision detection logic in symbol_sdf.vertex + // in the CPU + const float perspectiveScale = scale / perspectiveRatio; + // 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); + const float roundedScale = std::pow(2.0f, std::ceil(util::log2(perspectiveScale) * 10.0f) / 10.0f); // Check if feature is rendered (collision free) at current scale. auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool { @@ -203,10 +222,11 @@ std::vector CollisionTile::queryRenderedSymbols(const Geometr 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; + + const int16_t x1 = anchor.x + (collisionBox.x1 / perspectiveScale); + const int16_t y1 = anchor.y + (collisionBox.y1 / perspectiveScale) * yStretch; + const int16_t x2 = anchor.x + (collisionBox.x2 / perspectiveScale); + const int16_t y2 = anchor.y + (collisionBox.y2 / perspectiveScale) * yStretch; auto bbox = GeometryCoordinates { { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 } }; diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index 59fd1a39275..dbff6a007b0 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -49,8 +49,8 @@ class CollisionTile { const PlacementConfig config; - const float minScale = 0.5f; - const float maxScale = 2.0f; + float minScale = 0.5f; + float maxScale = 2.0f; float yStretch; std::array rotationMatrix; @@ -64,6 +64,8 @@ class CollisionTile { Tree tree; Tree ignoredTree; + + float perspectiveRatio; }; } // namespace mbgl diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp index 7e61cabc24e..c5c013055a9 100644 --- a/src/mbgl/text/placement_config.hpp +++ b/src/mbgl/text/placement_config.hpp @@ -4,12 +4,12 @@ namespace mbgl { class PlacementConfig { public: - PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false) - : angle(angle_), pitch(pitch_), debug(debug_) { + PlacementConfig(float angle_ = 0, float pitch_ = 0, float cameraToCenterDistance_ = 0, float cameraToTileDistance_ = 0, bool debug_ = false) + : angle(angle_), pitch(pitch_), cameraToCenterDistance(cameraToCenterDistance_), cameraToTileDistance(cameraToTileDistance_), debug(debug_) { } bool operator==(const PlacementConfig& rhs) const { - return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug; + return angle == rhs.angle && pitch == rhs.pitch && cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance && debug == rhs.debug; } bool operator!=(const PlacementConfig& rhs) const { @@ -19,6 +19,8 @@ class PlacementConfig { public: float angle; float pitch; + float cameraToCenterDistance; + float cameraToTileDistance; bool debug; }; diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 4ab11d79fed..ad5c2edd4c8 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -264,4 +264,8 @@ void GeometryTile::querySourceFeatures( } } +float GeometryTile::yStretch() const { + return collisionTile->yStretch; +} + } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 77202d20b6d..3e2efe10938 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -86,6 +86,8 @@ class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor { void onError(std::exception_ptr); + float yStretch() const override; + protected: const GeometryTileData* getData() { return data.get(); diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index a925d88af30..1898f768499 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -105,6 +105,8 @@ class Tile : private util::noncopyable { // Contains the tile ID string for painting debug information. std::unique_ptr debugBucket; + + virtual float yStretch() const { return 1.0f; } protected: bool triedOptional = false; From 8f5e0b66ab13cff7d35ed46afaddbdca9ab1993f Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 17 May 2017 21:40:57 -0700 Subject: [PATCH 02/15] [core] Pass pitch-scaling vertex attributes and uniforms to shaders. --- src/mbgl/layout/symbol_layout.cpp | 23 ++++++++++--------- src/mbgl/layout/symbol_layout.hpp | 3 ++- src/mbgl/programs/attributes.hpp | 2 ++ src/mbgl/programs/collision_box_program.cpp | 2 +- src/mbgl/programs/collision_box_program.hpp | 12 ++++++++-- src/mbgl/programs/symbol_program.cpp | 6 +++-- src/mbgl/programs/symbol_program.hpp | 21 +++++++++++++---- src/mbgl/programs/uniforms.hpp | 2 ++ src/mbgl/renderer/painters/painter_symbol.cpp | 3 +++ 9 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index adc7eaaed89..a6649574893 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -504,7 +504,7 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) for (const auto& symbol : symbolInstance.glyphQuads) { addSymbol( bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom, - keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes); + keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point); } } } @@ -515,7 +515,7 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { addSymbol( bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom, - keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); + keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point); } } @@ -541,7 +541,8 @@ void SymbolLayout::addSymbol(Buffer& buffer, const bool keepUpright, const style::SymbolPlacementType placement, const float placementAngle, - const WritingModeType writingModes) { + const WritingModeType writingModes, + const Point labelAnchor) { constexpr const uint16_t vertexLength = 4; const auto &tl = symbol.tl; @@ -590,13 +591,13 @@ void SymbolLayout::addSymbol(Buffer& buffer, 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, + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, labelAnchor, tex.x, tex.y, minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y, + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, labelAnchor, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h, + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, labelAnchor, 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, + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, labelAnchor, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom, glyphAngle)); sizeBinder.populateVertexVector(feature); @@ -646,10 +647,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& auto& segment = collisionBox.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.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tl, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tr, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, br, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, bl, maxZoom, placementZoom)); collisionBox.lines.emplace_back(index + 0, index + 1); collisionBox.lines.emplace_back(index + 1, index + 2); diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 4ee52e843fb..5dc0f3eb76f 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -65,7 +65,8 @@ class SymbolLayout { const bool keepUpright, const style::SymbolPlacementType, const float placementAngle, - WritingModeType writingModes); + WritingModeType writingModes, + const Point labelAnchor); // Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature, // which may reference data from this object. diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 8f2751080f4..f39af2deec2 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -23,6 +23,8 @@ 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, 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); diff --git a/src/mbgl/programs/collision_box_program.cpp b/src/mbgl/programs/collision_box_program.cpp index a3dc01ebe4a..57107db75dc 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 160fd428144..3b4260bb788 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -18,6 +18,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom); using CollisionBoxAttributes = gl::Attributes< attributes::a_pos, + attributes::a_anchor_pos, attributes::a_extrude, attributes::a_data>; @@ -29,18 +30,25 @@ class CollisionBoxProgram : public Program< uniforms::u_matrix, uniforms::u_scale, uniforms::u_zoom, - uniforms::u_maxzoom>, + uniforms::u_maxzoom, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, + uniforms::u_pitch>, style::Properties<>> { public: using Program::Program; - static LayoutVertex vertex(Point a, Point o, float maxzoom, float placementZoom) { + static LayoutVertex vertex(Point a, Point anchor, Point o, float maxzoom, float placementZoom) { return LayoutVertex { {{ static_cast(a.x), static_cast(a.y) }}, + {{ + static_cast(anchor.x), + static_cast(anchor.y) + }}, {{ static_cast(::round(o.x)), static_cast(::round(o.y)) diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 789eed0dd8a..1b5a7d9bffe 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -10,7 +10,7 @@ namespace mbgl { using namespace style; -static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size"); +static_assert(sizeof(SymbolLayoutVertex) == 20, "expected SymbolLayoutVertex size"); std::unique_ptr SymbolSizeBinder::create(const float tileZoom, const style::DataDrivenPropertyValue& sizeProperty, @@ -59,6 +59,9 @@ Values makeValues(const bool isText, uniforms::u_fadetexture::Value{ 1 }, uniforms::u_is_text::Value{ isText }, uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_max_camera_distance::Value{ 10.0f }, std::forward(args)... }; } @@ -103,7 +106,6 @@ typename SymbolSDFProgram::UniformValues SymbolSDFProgram> { static Vertex vertex(Point a, Point o, + Point labelAnchor, uint16_t tx, uint16_t ty, float minzoom, @@ -65,6 +67,10 @@ struct SymbolLayoutAttributes : gl::Attributes< static_cast(::round(o.x * 64)), // use 1/64 pixels for placement static_cast(::round(o.y * 64)) }}, + {{ + static_cast(labelAnchor.x), + static_cast(labelAnchor.y) + }}, {{ tx, ty, @@ -391,7 +397,10 @@ class SymbolIconProgram : public SymbolProgram< uniforms::u_texture, uniforms::u_fadetexture, uniforms::u_is_text, - uniforms::u_collision_y_stretch>, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, + uniforms::u_pitch, + uniforms::u_max_camera_distance>, style::IconPaintProperties> { public: @@ -425,8 +434,10 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_fadetexture, uniforms::u_is_text, uniforms::u_collision_y_stretch, - uniforms::u_gamma_scale, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, + uniforms::u_max_camera_distance, + uniforms::u_gamma_scale, uniforms::u_bearing, uniforms::u_aspect_ratio, uniforms::u_pitch_with_map, @@ -447,8 +458,10 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_fadetexture, uniforms::u_is_text, uniforms::u_collision_y_stretch, - uniforms::u_gamma_scale, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, + uniforms::u_max_camera_distance, + uniforms::u_gamma_scale, uniforms::u_bearing, uniforms::u_aspect_ratio, uniforms::u_pitch_with_map, diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index c8f8684ba17..8d606dcf08a 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -14,6 +14,8 @@ 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_camera_to_center_distance); MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing); MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index d3a505aa3f6..2d91ff41ad7 100644 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ b/src/mbgl/renderer/painters/painter_symbol.cpp @@ -152,6 +152,9 @@ void Painter::renderSymbol(PaintParameters& parameters, 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) }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ state.getPitch() } }, *bucket.collisionBox.vertexBuffer, *bucket.collisionBox.indexBuffer, From 7b29b90e7fb379ca4f592e98fd3144d424937632 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 18 May 2017 12:54:11 -0700 Subject: [PATCH 03/15] [core] Extend collision feature boxes to accommodate potential pitch-scaling. --- src/mbgl/text/collision_feature.cpp | 57 +++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp index 71d7cc74e06..58a7949a8ea 100644 --- a/src/mbgl/text/collision_feature.cpp +++ b/src/mbgl/text/collision_feature.cpp @@ -49,7 +49,11 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line, void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint, const int segment, const float labelLength, const float boxSize) { const float step = boxSize / 2; - const unsigned int nBoxes = std::floor(labelLength / step); + const int nBoxes = std::floor(labelLength / step); + // We calculate line collision boxes out to 150% of what would normally be our + // max size, to allow collision detection to work on labels that expand as + // they move into the distance + const int nPitchPaddingBoxes = std::floor(nBoxes / 4); // 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 +62,40 @@ 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(line[index], p); p = line[index]; - } while (anchorDistance > -labelLength / 2); + } while (anchorDistance > paddingStartDistance); auto segmentLength = util::dist(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 boxDistanceToAnchor = labelStartDistance + i * step; + 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) { @@ -99,8 +119,29 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y) }; + // Distance from label anchor point to inner (towards center) edge of this box + // The tricky thing here is that box positioning doesn't change with scale, + // but box size does change with scale. + // Technically, distanceToInnerEdge should be: + // Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0); + // But using that formula would make solving for maxScale more difficult, so we + // approximate with scale=2. + // This makes our calculation spot-on at scale=2, and on the conservative side for + // lower scales const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f); - const float maxScale = labelLength / 2 / distanceToInnerEdge; + float maxScale = labelLength / 2 / distanceToInnerEdge; + + // The box maxScale calculations are designed to be conservative on collisions in the scale range + // [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge + // to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions). + // We add "pitch padding" boxes to the left and right to handle effective underzooming + // (scale < 1) when labels are in the distance. The overlap approximation could cause us to use + // these boxes when the scale is greater than 1, but we prevent that because we know + // they're only necessary for scales less than one. + // This preserves the pre-pitch-padding behavior for unpitched maps. + if (i < 0 || i >= nBoxes) { + maxScale = std::min(maxScale, 0.99f); + } boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale); } From 2ee59796fb47c3152b2bdcfa5cde4be5a92316b4 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 18 May 2017 14:08:10 -0700 Subject: [PATCH 04/15] [core] Enable tile clipping for collision boxes. Necessary because collision boxes now change shape based on while tile they're part of. --- src/mbgl/renderer/painters/painter_symbol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index 2d91ff41ad7..c358d78b800 100644 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ b/src/mbgl/renderer/painters/painter_symbol.cpp @@ -145,7 +145,7 @@ void Painter::renderSymbol(PaintParameters& parameters, context, gl::Lines { 1.0f }, gl::DepthMode::disabled(), - gl::StencilMode::disabled(), + stencilModeForClipping(tile.clip), colorModeForRenderPass(), CollisionBoxProgram::UniformValues { uniforms::u_matrix::Value{ tile.matrix }, From f8402e20425cee6cbad2a28809bce17ac46c1379 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 19 May 2017 13:30:17 -0700 Subject: [PATCH 05/15] [core] Set "max_camera_distance" to 1.5 for viewport-aligned road labels. Viewport-aligned curved labels start to look very strange in the distance. Until we have a better system for projecting them, just prevent them from showing. --- src/mbgl/programs/symbol_program.cpp | 2 +- .../renderer/layers/render_symbol_layer.cpp | 18 ++++++++++++++++-- .../renderer/layers/render_symbol_layer.hpp | 2 ++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 1b5a7d9bffe..bd43237b8f0 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -61,7 +61,7 @@ Values makeValues(const bool isText, uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() }, uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_max_camera_distance::Value{ 10.0f }, + uniforms::u_max_camera_distance::Value{ values.maxCameraDistance }, std::forward(args)... }; } diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 6540fc96120..573e9db72ea 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -88,11 +88,24 @@ style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::S evaluated.get(), evaluated.get().constantOr(Color::black()).a > 0 && evaluated.get().constantOr(1), - evaluated.get().constantOr(Color::black()).a > 0 + evaluated.get().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::SymbolPlacementType::Line + && layout_.get() == style::AlignmentType::Map + && layout_.get() == style::AlignmentType::Viewport; + return style::SymbolPropertyValues { layout_.get(), layout_.get(), @@ -100,7 +113,8 @@ style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::S evaluated.get(), evaluated.get().constantOr(Color::black()).a > 0 && evaluated.get().constantOr(1), - evaluated.get().constantOr(Color::black()).a > 0 + evaluated.get().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 2199103de25..e788336cbdd 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -47,6 +47,8 @@ class SymbolPropertyValues { bool hasHalo; bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else }; } // namespace style From 8c23f14a9f47bc3e2d687ed25561b586643348f5 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 25 May 2017 09:15:27 -0700 Subject: [PATCH 06/15] [core] Use fade texture in collision debug boxes so that they agree more closely with symbol shaders. --- src/mbgl/programs/collision_box_program.hpp | 3 ++- src/mbgl/programs/symbol_program.hpp | 1 - src/mbgl/programs/uniforms.hpp | 1 + src/mbgl/renderer/painters/painter_symbol.cpp | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp index 3b4260bb788..ba99e0c0875 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -33,7 +33,8 @@ class CollisionBoxProgram : public Program< uniforms::u_maxzoom, uniforms::u_collision_y_stretch, uniforms::u_camera_to_center_distance, - uniforms::u_pitch>, + uniforms::u_pitch, + uniforms::u_fadetexture>, style::Properties<>> { public: diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index c38ed04a1ea..130e556b467 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -31,7 +31,6 @@ class TransformState; namespace uniforms { MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); 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); diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 8d606dcf08a..861f3271c99 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -51,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/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index c358d78b800..dc80f096f4d 100644 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ b/src/mbgl/renderer/painters/painter_symbol.cpp @@ -154,7 +154,8 @@ void Painter::renderSymbol(PaintParameters& parameters, uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() }, - uniforms::u_pitch::Value{ state.getPitch() } + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_fadetexture::Value{ 1 } }, *bucket.collisionBox.vertexBuffer, *bucket.collisionBox.indexBuffer, From 7c7564d72ed060bfd9ff1d5ebc6086a52f837c4b Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 16 May 2017 11:50:24 -0700 Subject: [PATCH 07/15] [core] Re-generate shaders. --- src/mbgl/shaders/collision_box.cpp | 36 +++++- src/mbgl/shaders/symbol_icon.cpp | 44 ++++--- src/mbgl/shaders/symbol_sdf.cpp | 193 ++++++++++++++++++----------- 3 files changed, 184 insertions(+), 89 deletions(-) diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 5f733c6a1e4..05f306ef657 100644 --- a/src/mbgl/shaders/collision_box.cpp +++ b/src/mbgl/shaders/collision_box.cpp @@ -8,44 +8,74 @@ 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; uniform mat4 u_matrix; uniform float u_scale; +uniform float u_pitch; +uniform float u_collision_y_stretch; +uniform float u_camera_to_center_distance; varying float v_max_zoom; varying float v_placement_zoom; +varying float v_perspective_zoom_adjust; +varying vec2 v_fade_tex; 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 = 1.0 + 0.5 * ((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + gl_Position = u_matrix * vec4(a_pos + a_extrude * collision_perspective_ratio * collision_adjustment / u_scale, 0.0, 1.0); v_max_zoom = a_data.x; v_placement_zoom = a_data.y; + + v_perspective_zoom_adjust = log2(collision_perspective_ratio * collision_adjustment) * 10.0; + v_fade_tex = vec2((v_placement_zoom + v_perspective_zoom_adjust) / 255.0, 0.0); } )MBGL_SHADER"; const char* collision_box::fragmentSource = R"MBGL_SHADER( uniform float u_zoom; +// u_maxzoom is derived from the maximum scale considered by the CollisionTile +// Labels with placement zoom greater than this value will not be placed, +// regardless of perspective effects. uniform float u_maxzoom; +uniform sampler2D u_fadetexture; +// v_max_zoom is a collision-box-specific value that controls when line-following +// collision boxes are used. varying float v_max_zoom; varying float v_placement_zoom; +varying float v_perspective_zoom_adjust; +varying vec2 v_fade_tex; void main() { float alpha = 0.5; + // Green = no collisions, label is showing gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha; - if (v_placement_zoom > u_zoom) { + // Red = collision, label hidden + if (texture2D(u_fadetexture, v_fade_tex).a < 1.0) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha; } - if (u_zoom >= v_max_zoom) { + // Faded black = this collision box is not used at this zoom (for curved labels) + if (u_zoom >= v_max_zoom + v_perspective_zoom_adjust) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25; } + // Faded blue = the placement scale for this label is beyond the CollisionTile + // max scale, so it's impossible for this label to show without collision detection + // being run again (the label's glyphs haven't even been added to the symbol bucket) if (v_placement_zoom >= u_maxzoom) { gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2; } diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index bc570cf361c..8960e02c28e 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -7,17 +7,20 @@ namespace shaders { const char* symbol_icon::name = "symbol_icon"; const char* symbol_icon::vertexSource = R"MBGL_SHADER( - attribute vec4 a_pos_offset; +attribute vec2 a_label_pos; attribute vec4 a_data; // 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_layout_size; // used when size is feature constant +uniform highp float u_camera_to_center_distance; +uniform highp float u_pitch; +uniform highp float u_collision_y_stretch; #ifndef HAS_UNIFORM_u_opacity @@ -32,7 +35,7 @@ uniform lowp float u_opacity; uniform mat4 u_matrix; uniform bool u_is_text; -uniform mediump float u_zoom; +uniform highp float u_zoom; uniform bool u_rotate_with_map; uniform vec2 u_extrude_scale; @@ -53,11 +56,11 @@ void main() { 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]; + highp vec2 label_data = unpack_float(a_data[2]); + highp float a_labelminzoom = label_data[0]; + highp vec2 a_zoom = unpack_float(a_data[3]); + highp float a_minzoom = a_zoom[0]; + highp float a_maxzoom = a_zoom[1]; float size; // In order to accommodate placing labels around corners in @@ -68,7 +71,7 @@ void main() { // 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; + highp 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; @@ -85,12 +88,16 @@ void main() { float fontScale = u_is_text ? size / 24.0 : size; - mediump float zoomAdjust = log2(size / layoutSize); - mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; + highp float zoomAdjust = log2(size / layoutSize); + highp 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 z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); + + vec4 projectedPoint = u_matrix * vec4(a_label_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + highp float perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (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; @@ -99,7 +106,12 @@ void main() { } v_tex = a_tex / u_texsize; - v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); + // See comments in symbol_sdf.vertex + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + highp float perspective_zoom_adjust = log2(perspective_ratio * collision_adjustment) * 10.0; + v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); } )MBGL_SHADER"; diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index cce6b769a67..bae01a5b592 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -9,7 +9,10 @@ const char* symbol_sdf::name = "symbol_sdf"; const char* symbol_sdf::vertexSource = R"MBGL_SHADER( const float PI = 3.141592653589793; +// NOTE: the a_data attribute in this shader is manually bound (see https://github.com/mapbox/mapbox-gl-js/issues/4607). +// If removing or renaming a_data, revisit the manual binding in painter.js accordingly. attribute vec4 a_pos_offset; +attribute vec2 a_label_pos; attribute vec4 a_data; // contents of a_size vary based on the type of property value @@ -23,12 +26,12 @@ attribute vec4 a_data; 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_layout_size; // used when size is feature constant -#ifndef HAS_UNIFORM_u_fill_color +#ifdef HAS_UNIFORM_u_fill_color uniform lowp float a_fill_color_t; attribute highp vec4 a_fill_color; varying highp vec4 fill_color; @@ -36,7 +39,8 @@ varying highp vec4 fill_color; uniform highp vec4 u_fill_color; #endif -#ifndef HAS_UNIFORM_u_halo_color + +#ifdef HAS_UNIFORM_u_halo_color uniform lowp float a_halo_color_t; attribute highp vec4 a_halo_color; varying highp vec4 halo_color; @@ -44,7 +48,8 @@ varying highp vec4 halo_color; uniform highp vec4 u_halo_color; #endif -#ifndef HAS_UNIFORM_u_opacity + +#ifdef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; varying lowp float opacity; @@ -52,7 +57,8 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -#ifndef HAS_UNIFORM_u_halo_width + +#ifdef HAS_UNIFORM_u_halo_width uniform lowp float a_halo_width_t; attribute lowp vec2 a_halo_width; varying lowp float halo_width; @@ -60,7 +66,8 @@ varying lowp float halo_width; uniform lowp float u_halo_width; #endif -#ifndef HAS_UNIFORM_u_halo_blur + +#ifdef HAS_UNIFORM_u_halo_blur uniform lowp float a_halo_blur_t; attribute lowp vec2 a_halo_blur; varying lowp float halo_blur; @@ -68,68 +75,89 @@ varying lowp float halo_blur; uniform lowp float u_halo_blur; #endif + // matrix is for the vertex position. uniform mat4 u_matrix; uniform bool u_is_text; -uniform mediump float u_zoom; +uniform highp 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 highp float u_pitch; +uniform highp float u_bearing; +uniform highp float u_aspect_ratio; +uniform highp float u_camera_to_center_distance; +uniform highp float u_max_camera_distance; +uniform highp float u_collision_y_stretch; uniform vec2 u_extrude_scale; uniform vec2 u_texsize; -varying vec4 v_data0; -varying vec2 v_data1; +varying vec2 v_tex; +varying vec2 v_fade_tex; +varying float v_gamma_scale; +varying float v_size; + +// Used below to move the vertex out of the clip space for when the current +// zoom is out of the glyph's zoom range. +highp float clipUnusedGlyphAngles(const highp float render_size, + const highp float layout_size, + const highp float min_zoom, + const highp float max_zoom) { + highp float zoom_adjust = log2(render_size / layout_size); + highp float adjusted_zoom = (u_zoom - zoom_adjust) * 10.0; + // result: 0 if min_zoom <= adjusted_zoom < max_zoom, and 1 otherwise + return 2.0 - step(min_zoom, adjusted_zoom) - (1.0 - step(max_zoom, adjusted_zoom)); +} 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; - 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]; - float size; + highp vec2 label_data = unpack_float(a_data[2]); + highp float a_labelminzoom = label_data[0]; + highp float a_lineangle = (label_data[1] / 256.0 * 2.0 * PI); + highp vec2 a_zoom = unpack_float(a_data[3]); + highp float a_minzoom = a_zoom[0]; + highp float a_maxzoom = a_zoom[1]; // In order to accommodate placing labels around corners in // symbol-placement: line, each glyph in a label could have multiple @@ -139,79 +167,97 @@ void main() { // 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; + highp 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; + v_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; + v_size = a_size[0] / 10.0; + layoutSize = v_size; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { - size = u_size; + v_size = u_size; layoutSize = u_layout_size; } else { - size = u_size; + v_size = u_size; layoutSize = u_size; } - float fontScale = u_is_text ? size / 24.0 : size; + float fontScale = u_is_text ? v_size / 24.0 : v_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)); + vec4 projectedPoint = u_matrix * vec4(a_label_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + highp float perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); // 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); + highp float angle = u_rotate_with_map ? a_lineangle : u_bearing; + highp float asin = sin(angle); + highp 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); + vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (offset / 64.0); + gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += z * gl_Position.w; + gl_Position.z += clipUnusedGlyphAngles(v_size*perspective_ratio, layoutSize, a_minzoom, a_maxzoom) * 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; + highp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); // 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); + vec4 b = u_matrix * vec4(a_pos + vec2(cos(a_lineangle), sin(a_lineangle)), 0, 1); + highp float angle = atan((b[1] / b[3] - a[1] / a[3]) / u_aspect_ratio, b[0] / b[3] - a[0] / a[3]); + highp float asin = sin(angle); + highp float acos = cos(angle); mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); + highp float foreshortening = (1.0 - pitchfactor) + (pitchfactor * cos(angle * 2.0)); + + vec2 offset = RotationMatrix * (vec2(foreshortening, 1.0) * a_offset); + vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (offset / 64.0); - 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; + gl_Position.z += clipUnusedGlyphAngles(v_size * perspective_ratio, layoutSize, a_minzoom, a_maxzoom) * gl_Position.w; // pitch-alignment: viewport // rotation-alignment: viewport } else { - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (a_offset / 64.0); gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); } - float gamma_scale = gl_Position.w; - - vec2 tex = a_tex / u_texsize; - vec2 fade_tex = vec2(a_labelminzoom / 255.0, 0.0); - - v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y); - v_data1 = vec2(gamma_scale, size); + gl_Position.z += + step(u_max_camera_distance * u_camera_to_center_distance, camera_to_anchor_distance) * gl_Position.w; + + v_gamma_scale = gl_Position.w / perspective_ratio; + + v_tex = a_tex / u_texsize; + // incidence_stretch 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) + // sin(incidence_angle) = 1/incidence_stretch + // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no incidence stretch + // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of incidence stretch + // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch) + // This 2D calculation is only exactly correct when gl_Position.x is in the center of the viewport, + // but it's a close enough approximation for our purposes + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + // incidence_stretch only applies to the y-axis, but without re-calculating the collision tile, we can't + // adjust the size of only one axis. So, we do a crude approximation at placement time to get the aspect ratio + // about right, and then do the rest of the adjustment here: there will be some extra padding on the x-axis, + // but hopefully not too much. + // Never make the adjustment less than 1.0: instead of allowing collisions on the x-axis, be conservative on + // the y-axis. + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + // Floor to 1/10th zoom to dodge precision issues that can cause partially hidden labels + highp float perspective_zoom_adjust = floor(log2(perspective_ratio * collision_adjustment) * 10.0); + v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); } )MBGL_SHADER"; @@ -227,66 +273,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_tex; +varying vec2 v_fade_tex; +varying float v_gamma_scale; +varying float v_size; 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 fontScale = u_is_text ? size / 24.0 : size; + float fontScale = u_is_text ? v_size / 24.0 : v_size; lowp vec4 color = fill_color; highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale); @@ -297,9 +350,9 @@ void main() { buff = (6.0 - halo_width / fontScale) / SDF_PX; } - 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; + lowp float dist = texture2D(u_texture, v_tex).a; + lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; + highp float gamma_scaled = gamma * v_gamma_scale; highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha; gl_FragColor = color * (alpha * opacity); From 72f6b73590722d5451623cd21c4b1abde74e8f69 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 25 May 2017 14:12:20 -0700 Subject: [PATCH 08/15] [core] Limit symbol re-placement on changed tile distance to pitch > 25. --- src/mbgl/text/placement_config.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp index c5c013055a9..1e1279341d3 100644 --- a/src/mbgl/text/placement_config.hpp +++ b/src/mbgl/text/placement_config.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace mbgl { class PlacementConfig { @@ -9,7 +11,11 @@ class PlacementConfig { } bool operator==(const PlacementConfig& rhs) const { - return angle == rhs.angle && pitch == rhs.pitch && cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance && debug == rhs.debug; + return angle == rhs.angle && + pitch == rhs.pitch && + cameraToCenterDistance == rhs.cameraToCenterDistance && + (pitch * util::RAD2DEG < 25 || cameraToTileDistance == rhs.cameraToTileDistance) && + debug == rhs.debug; } bool operator!=(const PlacementConfig& rhs) const { From a432a289a35815beeadc8719a963f50f8dc07bbb Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 29 May 2017 12:47:32 -0700 Subject: [PATCH 09/15] [core] Change OverscaledTileID to also include a "wrap" value. This prevents TilePyramid from sharing wrapped copies of tiles. This is necessary because two wrapped tiles no longer share the same CollisionTile. --- src/mbgl/algorithm/update_renderables.hpp | 9 +- src/mbgl/renderer/tile_pyramid.cpp | 7 +- src/mbgl/tile/tile_id.hpp | 33 +- test/algorithm/update_renderables.test.cpp | 986 ++++++++++----------- test/tile/tile_id.test.cpp | 146 +-- 5 files changed, 588 insertions(+), 593 deletions(-) diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index a9c348b5384..0c2266ff477 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -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); @@ -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); + 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 diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 57e7f6d375c..5b1e6217438 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -176,15 +176,12 @@ void TilePyramid::update(const std::vector>& layer removeStaleTiles(retain); for (auto& pair : tiles) { - // TODO: Calculating yStretch based on tile distance means we can no longer use the same collision tile for two wrapped - // copies of the same data tile. For now the assumption that we're always at "wrap = 0" means collision detection is broken - // for wrapped tiles. const PlacementConfig config { parameters.transformState.getAngle(), parameters.transformState.getPitch(), parameters.transformState.getCameraToCenterDistance(), - parameters.transformState.getCameraToTileDistance(pair.first.unwrapTo(0)), + parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()), parameters.debugOptions & MapDebugOptions::Collision }; - + pair.second->setPlacementConfig(config); } } diff --git a/src/mbgl/tile/tile_id.hpp b/src/mbgl/tile/tile_id.hpp index 1ce3eea98ed..811158e9b9c 100644 --- a/src/mbgl/tile/tile_id.hpp +++ b/src/mbgl/tile/tile_id.hpp @@ -46,8 +46,8 @@ std::string toString(const CanonicalTileID&); // 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 overscaledZ, int16_t wrap, CanonicalTileID); + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); explicit OverscaledTileID(const CanonicalTileID&); explicit OverscaledTileID(CanonicalTileID&&); @@ -57,9 +57,10 @@ class OverscaledTileID { bool isChildOf(const OverscaledTileID&) const; uint32_t overscaleFactor() const; OverscaledTileID scaledTo(uint8_t z) const; - UnwrappedTileID unwrapTo(int16_t wrap) const; + UnwrappedTileID toUnwrapped() const; const uint8_t overscaledZ; + const int16_t wrap; const CanonicalTileID canonical; }; @@ -137,40 +138,40 @@ inline std::array CanonicalTileID::children() const { } }; } -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID canonical_) - : overscaledZ(overscaledZ_), canonical(std::move(canonical_)) { +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) { assert(overscaledZ >= canonical.z); } -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(overscaledZ_), canonical(z, x, y) { +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { assert(overscaledZ >= canonical.z); } inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(z), canonical(z, x, y) { + : overscaledZ(z), wrap(0), canonical(z, x, y) { } inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) - : overscaledZ(canonical_.z), canonical(canonical_) { + : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { assert(overscaledZ >= canonical.z); } inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) - : overscaledZ(canonical_.z), canonical(std::forward(canonical_)) { + : overscaledZ(canonical_.z), wrap(0), canonical(std::forward(canonical_)) { assert(overscaledZ >= canonical.z); } inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { - return overscaledZ == rhs.overscaledZ && canonical == rhs.canonical; + return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; } inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { - return overscaledZ != rhs.overscaledZ || canonical != rhs.canonical; + return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; } inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { - return std::tie(overscaledZ, canonical) < std::tie(rhs.overscaledZ, rhs.canonical); + return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); } inline uint32_t OverscaledTileID::overscaleFactor() const { @@ -183,10 +184,10 @@ inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { } inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { - return { z, z >= canonical.z ? canonical : canonical.scaledTo(z) }; + return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; } -inline UnwrappedTileID OverscaledTileID::unwrapTo(int16_t wrap) const { +inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { return { wrap, canonical }; } @@ -232,7 +233,7 @@ inline std::array UnwrappedTileID::children() const { inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { assert(overscaledZ >= canonical.z); - return { overscaledZ, canonical }; + return { overscaledZ, wrap, canonical }; } inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { diff --git a/test/algorithm/update_renderables.test.cpp b/test/algorithm/update_renderables.test.cpp index af90d262de8..26b7cf7f721 100644 --- a/test/algorithm/update_renderables.test.cpp +++ b/test/algorithm/update_renderables.test.cpp @@ -23,10 +23,10 @@ struct GetTileDataAction { }; std::ostream& operator<<(std::ostream& os, const GetTileDataAction& action) { - return os << "GetTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { " + return os << "GetTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { " << int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", " << action.tileID.canonical.y << " } }, " - << (action.found == Found ? "Found" : "NotFound") << " }"; + << (action.found == Found ? "Found" : "NotFound") << " }\n"; } struct CreateTileDataAction { @@ -38,9 +38,9 @@ struct CreateTileDataAction { }; std::ostream& operator<<(std::ostream& os, const CreateTileDataAction& action) { - return os << "CreateTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { " + return os << "CreateTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { " << int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", " - << action.tileID.canonical.y << " } } }"; + << action.tileID.canonical.y << " } } }\n"; } struct RetainTileDataAction { @@ -53,10 +53,10 @@ struct RetainTileDataAction { }; std::ostream& operator<<(std::ostream& os, const RetainTileDataAction& action) { - return os << "RetainTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { " + return os << "RetainTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { " << int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", " << action.tileID.canonical.y << " } }, " - << (action.necessity == Resource::Necessity::Required ? "Required" : "Optional") << " }"; + << (action.necessity == Resource::Necessity::Required ? "Required" : "Optional") << " }\n"; } struct RenderTileAction { @@ -76,7 +76,7 @@ std::ostream& operator<<(std::ostream& os, const RenderTileAction& action) { << int(action.tileData.tileID.overscaledZ) << "_" << int(action.tileData.tileID.canonical.z) << "_" << action.tileData.tileID.canonical.x << "_" << action.tileData.tileID.canonical.y - << " }"; + << " }\n"; } using ActionLogEntry = @@ -129,8 +129,8 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile }), log); @@ -140,8 +140,8 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile }), log); @@ -152,39 +152,39 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile - CreateTileDataAction{ { 1, { 1, 0, 1 } } }, // create ideal tile - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles - GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile - - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // missing ideal tile + CreateTileDataAction{ { 1, 0, { 1, 0, 1 } } }, // create ideal tile + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // four child tiles + GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile + + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile }), log); // Mark the created tile as having the optional request tried. log.clear(); - source.dataTiles[{ 1, { 1, 0, 1 } }]->triedOptional = true; + source.dataTiles[{ 1, 0, { 1, 0, 1 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // missing ideal tile - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles - GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile - CreateTileDataAction{ { 0, { 0, 0, 0 } } }, // load parent tile - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // - - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // missing ideal tile + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // four child tiles + GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile + CreateTileDataAction{ { 0, 0, { 0, 0, 0 } } }, // load parent tile + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile }), log); @@ -196,12 +196,12 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // newly added tile - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // newly added tile + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile }), log); @@ -215,22 +215,22 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, not ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // four child tiles - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // found tile, not ready + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // four child tiles + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // // optional parent tile was already created before, but is not renderable - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile }), log); @@ -241,16 +241,16 @@ TEST(UpdateRenderables, SingleTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, now ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // found tile, now ready + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // - GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // }), log); @@ -274,30 +274,30 @@ TEST(UpdateRenderables, UseParentTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile - CreateTileDataAction{ { 1, { 1, 0, 1 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile - GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent found! - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // missing ideal tile + CreateTileDataAction{ { 1, 0, { 1, 0, 1 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // child tile + GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent found! + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent - GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // missing ideal tile - CreateTileDataAction{ { 1, { 1, 1, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // missing tile - CreateTileDataAction{ { 1, { 1, 1, 1 } } }, // - RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 2, 2 } }, NotFound }, // child tile - GetTileDataAction{ { 2, { 2, 2, 3 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 3, 2 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 3, 3 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, NotFound }, // missing ideal tile + CreateTileDataAction{ { 1, 0, { 1, 1, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 2, 0, { 2, 2, 1 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 3, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 3, 1 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, NotFound }, // missing tile + CreateTileDataAction{ { 1, 0, { 1, 1, 1 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 2, 2 } }, NotFound }, // child tile + GetTileDataAction{ { 2, 0, { 2, 2, 3 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 3, 2 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 3, 3 } }, NotFound }, // ... }), log); } @@ -317,34 +317,34 @@ TEST(UpdateRenderables, DontUseWrongParentTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // missing ideal tile - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // missing ideal tile + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // parent tile, missing + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing }), log); // Now mark the created tile as having the optional request tried. log.clear(); - source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // find optional parent - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // non-ready ideal tile + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // parent tile, missing + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // find optional parent + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing }), log); @@ -354,26 +354,26 @@ TEST(UpdateRenderables, DontUseWrongParentTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // non-ready ideal tile + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // // this tile was added by the previous invocation of updateRenderables - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile not ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // missing parent tile - - GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // missing ideal tile - CreateTileDataAction{ { 2, { 2, 2, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 2, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 3, 4, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 3, { 3, 4, 1 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 5, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 5, 1 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // found parent tile - RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // parent tile not ready + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // missing parent tile + + GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // missing ideal tile + CreateTileDataAction{ { 2, 0, { 2, 2, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 2, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 3, 4, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 3, 0, { 3, 4, 1 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 5, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 5, 1 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, Found }, // found parent tile + RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render parent tile }), log); @@ -399,14 +399,14 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found, but not ready - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile - GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, ready - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // found, but not ready + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // child tile + GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, ready + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent tile }), log); @@ -417,8 +417,8 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found and ready - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // found and ready + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile }), log); @@ -444,19 +444,19 @@ TEST(UpdateRenderables, UseOverlappingParentTile) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile not found - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile found - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile not found + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile found + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // - GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile found - RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile found + RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, Resource::Necessity::Required }, // RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // }), log); @@ -480,17 +480,17 @@ TEST(UpdateRenderables, UseChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 0); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ideal tile, missing - CreateTileDataAction{ { 0, { 0, 0, 0 } } }, // - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // child tile found - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ideal tile, missing + CreateTileDataAction{ { 0, 0, { 0, 0, 0 } } }, // + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // child tile found + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // render child tile - GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // child tile not found - GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // child tile found - RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // child tile not found + GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, Found }, // child tile found + RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render child tile - GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // child tile not found + GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, NotFound }, // child tile not found // no parent tile of 0 to consider }), log); @@ -514,17 +514,17 @@ TEST(UpdateRenderables, PreferChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, not found - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile, not found + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // child tile, not found - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // child tile, not found + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // }), log); @@ -537,19 +537,19 @@ TEST(UpdateRenderables, PreferChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready // ideal tile was added in previous invocation, but is not yet ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, Resource::Necessity::Optional }, // ... RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // child tile, not found - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // child tile, not found + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // }), log); @@ -560,21 +560,21 @@ TEST(UpdateRenderables, PreferChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready // ideal tile was added in first invocation, but is not yet ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 1, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 1, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // child tile, not found - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // child tile, not found + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // }), log); @@ -586,20 +586,20 @@ TEST(UpdateRenderables, PreferChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready // ideal tile was added in first invocation, but is not yet ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 1, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 1, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, Found }, // ... - RetainTileDataAction{ { 2, { 2, 1, 1 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, Found }, // ... + RetainTileDataAction{ { 2, 0, { 2, 1, 1 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 1, 1 }, *tile_2_2_1_1 }, // }), log); @@ -624,17 +624,17 @@ TEST(UpdateRenderables, UseParentAndChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, missing - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile, missing + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // }), log); @@ -645,14 +645,14 @@ TEST(UpdateRenderables, UseParentAndChildTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // }), log); @@ -675,13 +675,13 @@ TEST(UpdateRenderables, DontUseTilesLowerThanMinzoom) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // // no requests for zoom 1 tiles }), log); @@ -705,58 +705,58 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) { source.idealTiles, source.zoomRange, 2); EXPECT_EQ( ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children! - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children! + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // }), log); // Mark the created tile as having tried the optional request. log.clear(); - source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ( ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, missing - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children! - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // ideal tile, missing + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children! + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // }), log); // Only add a non-overzoomed ("parent") tile at first. log.clear(); - auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } }); + auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, { 2, 0, 0 } }); tile_2_2_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // ideal tile, missing - CreateTileDataAction{ { 3, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing + CreateTileDataAction{ { 3, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // }), log); // Then add the overzoomed tile matching the zoom level we're rendering. log.clear(); - auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } }); + auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, 0, { 2, 0, 0 } }); tile_3_2_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, // }), log); @@ -766,26 +766,26 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // }), log); // Now remove the best match. log.clear(); - source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } }); + source.dataTiles.erase(OverscaledTileID{ 2, 0, { 2, 0, 0 } }); tile_2_2_0_0 = nullptr; // Use the overzoomed tile even though it doesn't match the zoom level. algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // use overzoomed tile! - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // use overzoomed tile! + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, // }), log); @@ -803,69 +803,69 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) { source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 }); // Add a matching overzoomed tile and make sure it gets selected. - auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } }); + auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, 0, { 2, 0, 0 } }); tile_3_2_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, // }), log); // Then, swap it with a non-overzoomed tile. log.clear(); - source.dataTiles.erase(OverscaledTileID{ 3, { 2, 0, 0 } }); + source.dataTiles.erase(OverscaledTileID{ 3, 0, { 2, 0, 0 } }); tile_3_2_0_0 = nullptr; - auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } }); + auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, { 2, 0, 0 } }); tile_2_2_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // - CreateTileDataAction{ { 3, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // prefer using a child first - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // + CreateTileDataAction{ { 3, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // prefer using a child first + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, // }), log); // Then, swap it with a parent tile. log.clear(); - source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } }); + source.dataTiles.erase(OverscaledTileID{ 2, 0, { 2, 0, 0 } }); tile_2_2_0_0 = nullptr; - auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, { 1, 0, 0 } }); + auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, { 1, 0, 0 } }); tile_1_1_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // }), log); // Now, mark the created tile as found. log.clear(); - source.dataTiles[{ 3, { 2, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 3, 0, { 2, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 3); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // }), log); @@ -885,60 +885,60 @@ TEST(UpdateRenderables, DoNotAscendMultipleTimesIfNotFound) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 8); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 8, { 8, 0, 0 } }, NotFound }, // ideal tile - CreateTileDataAction{ { 8, { 8, 0, 0 } } }, // - RetainTileDataAction{ { 8, { 8, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent - GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... - - GetTileDataAction{ { 8, { 8, 1, 0 } }, NotFound }, // ideal tile - CreateTileDataAction{ { 8, { 8, 1, 0 } } }, // - RetainTileDataAction{ { 8, { 8, 1, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ... + GetTileDataAction{ { 8, 0, { 8, 0, 0 } }, NotFound }, // ideal tile + CreateTileDataAction{ { 8, 0, { 8, 0, 0 } } }, // + RetainTileDataAction{ { 8, 0, { 8, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 9, 0, { 9, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 9, 0, { 9, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // ascent + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... + + GetTileDataAction{ { 8, 0, { 8, 1, 0 } }, NotFound }, // ideal tile + CreateTileDataAction{ { 8, 0, { 8, 1, 0 } } }, // + RetainTileDataAction{ { 8, 0, { 8, 1, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 9, 0, { 9, 2, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 9, 0, { 9, 2, 1 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 3, 0 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 3, 1 } }, NotFound }, // ... // no second ascent to 0 }), log); // Now add a mid-level tile that stops the ascent log.clear(); - auto tile_4_0_0_0 = source.createTileData(OverscaledTileID{ 4, { 4, 0, 0 } }); + auto tile_4_0_0_0 = source.createTileData(OverscaledTileID{ 4, 0, { 4, 0, 0 } }); tile_4_0_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 8); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 8, { 8, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 8, { 8, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent - GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // stops ascent - RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 8, 0, { 8, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 8, 0, { 8, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 9, 0, { 9, 0, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 9, 0, { 9, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // ascent + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // stops ascent + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 4, 0, 0 }, *tile_4_0_0_0 }, // - GetTileDataAction{ { 8, { 8, 1, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 8, { 8, 1, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile - GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ... - GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ... + GetTileDataAction{ { 8, 0, { 8, 1, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 8, 0, { 8, 1, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 9, 0, { 9, 2, 0 } }, NotFound }, // child tile + GetTileDataAction{ { 9, 0, { 9, 2, 1 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 3, 0 } }, NotFound }, // ... + GetTileDataAction{ { 9, 0, { 9, 3, 1 } }, NotFound }, // ... // no second ascent to 0 }), log); @@ -960,15 +960,15 @@ TEST(UpdateRenderables, DontRetainUnusedNonIdealTiles) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 2); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile, not ready - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // parent tile, not ready + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // }), log); } @@ -981,56 +981,54 @@ TEST(UpdateRenderables, WrappedTiles) { auto retainTileData = retainTileDataFn(log); auto renderTile = renderTileFn(log); - source.idealTiles.emplace(UnwrappedTileID{ 1, -1, 0 }); - source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); - source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 }); - source.idealTiles.emplace(UnwrappedTileID{ 1, 2, 0 }); + source.idealTiles.emplace(UnwrappedTileID{ 1, -1, 0 }); // 'wrap' -> -1 + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); // 'wrap' -> 0 + source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 }); // 'wrap' -> 0 + source.idealTiles.emplace(UnwrappedTileID{ 1, 2, 0 }); // 'wrap' -> 1 - auto tile_0_0_0_0 = source.createTileData(OverscaledTileID{ 0, { 0, 0, 0 } }); + auto tile_0_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, { 0, 0, 0 } }); tile_0_0_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 1); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // ideal tile 1/-1/0 - CreateTileDataAction{ { 1, { 1, 1, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // - RenderTileAction{ { 0, -1, 0 }, *tile_0_0_0_0 }, // - - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile 1/0/0 - CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, -1, { 1, 1, 0 } }, NotFound }, // ideal tile 1/-1/0 (wrapped to -1) + CreateTileDataAction{ { 1, -1, { 1, 1, 0 } } }, // + RetainTileDataAction{ { 1, -1, { 1, 1, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, -1, { 2, 2, 0 } }, NotFound }, // + GetTileDataAction{ { 2, -1, { 2, 2, 1 } }, NotFound }, // + GetTileDataAction{ { 2, -1, { 2, 3, 0 } }, NotFound }, // + GetTileDataAction{ { 2, -1, { 2, 3, 1 } }, NotFound }, // + GetTileDataAction{ { 0, -1, { 0, 0, 0 } }, NotFound }, // { 0, 0, 0 } exists, but not the version wrapped to -1 + + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile 1/0/0 + CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // + RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // + RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // - GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // ideal tile 1/1/0 - RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, // + GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, NotFound }, // ideal tile 1/1/0, doesn't match 1/-/1/0 + CreateTileDataAction{ { 1, 0, { 1, 1, 0 } } }, + RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 2, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 3, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 0, { 2, 3, 1 } }, NotFound }, // // do not ascent; 0/0/0 has been rendered already for 1/0/0 - GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile 1/2/0 - RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // - GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // - GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // - RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, // - RenderTileAction{ { 0, 1, 0 }, *tile_0_0_0_0 }, // + GetTileDataAction{ { 1, 1, { 1, 0, 0 } }, NotFound }, // ideal tile 1/2/0 (wrapped to 1) + CreateTileDataAction{ { 1, 1, { 1, 0, 0 } } }, + RetainTileDataAction{ { 1, 1, { 1, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 2, 1, { 2, 0, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 1, { 2, 0, 1 } }, NotFound }, // + GetTileDataAction{ { 2, 1, { 2, 1, 0 } }, NotFound }, // + GetTileDataAction{ { 2, 1, { 2, 1, 1 } }, NotFound }, // + GetTileDataAction{ { 0, 1, { 0, 0, 0 } }, NotFound }, // { 0, 0, 0 } exists, but not the version wrapped to -1 }), log); } @@ -1048,19 +1046,19 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ideal tile, not found - CreateTileDataAction{ { 6, { 6, 0, 0 } } }, // - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ideal tile, not found + CreateTileDataAction{ { 6, 0, { 6, 0, 0 } } }, // + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); @@ -1069,41 +1067,41 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 6, { 6, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 6, 0, { 6, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent - CreateTileDataAction{ { 5, { 5, 0, 0 } } }, // - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent + CreateTileDataAction{ { 5, 0, { 5, 0, 0 } } }, // + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); @@ -1112,116 +1110,116 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) { algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 5, { 5, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 5, 0, { 5, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - CreateTileDataAction{ { 4, { 4, 0, 0 } } }, // - RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + CreateTileDataAction{ { 4, 0, { 4, 0, 0 } } }, // + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 4, { 4, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 4, 0, { 4, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - CreateTileDataAction{ { 3, { 3, 0, 0 } } }, // - RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + CreateTileDataAction{ { 3, 0, { 3, 0, 0 } } }, // + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); // Mark next level has having tried optional. log.clear(); - source.dataTiles[{ 3, { 3, 0, 0 } }]->triedOptional = true; + source.dataTiles[{ 3, 0, { 3, 0, 0 } }]->triedOptional = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - CreateTileDataAction{ { 2, { 2, 0, 0 } } }, // - RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ... + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, // + RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); // Mark as found log.clear(); - auto tile_3_3_0_0 = source.dataTiles[{ 3, { 3, 0, 0 } }].get(); + auto tile_3_3_0_0 = source.dataTiles[{ 3, 0, { 3, 0, 0 } }].get(); tile_3_3_0_0->renderable = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children - GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ... - GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ... - GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, // - GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ... - RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children + GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ... + GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ... + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ... + RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, // + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ... + RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, // RenderTileAction{ { 3, 0, 0 }, *tile_3_3_0_0 }, // }), log); @@ -1238,24 +1236,24 @@ TEST(UpdateRenderables, LoadRequiredIfIdealTileCantBeFound) { source.zoomRange.max = 6; source.idealTiles.emplace(UnwrappedTileID{ 6, 0, 0 }); - auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, { 6, 0, 0 } }); + auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, 0, { 6, 0, 0 } }); tile_6_6_0_0->triedOptional = true; tile_6_6_0_0->loaded = true; algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile, source.idealTiles, source.zoomRange, 6); EXPECT_EQ(ActionLog({ - GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not found - RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, // - GetTileDataAction{ { 7, { 6, 0, 0 } }, NotFound }, // overzoomed child - GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent - CreateTileDataAction{ { 5, { 5, 0, 0 } } }, - RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Required }, - GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ... - GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not found + RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, Resource::Necessity::Required }, // + GetTileDataAction{ { 7, 0, { 6, 0, 0 } }, NotFound }, // overzoomed child + GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent + CreateTileDataAction{ { 5, 0, { 5, 0, 0 } } }, + RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, Resource::Necessity::Required }, + GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ... + GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ... }), log); } diff --git a/test/tile/tile_id.test.cpp b/test/tile/tile_id.test.cpp index 1ef19fea0ed..2f328b78d75 100644 --- a/test/tile/tile_id.test.cpp +++ b/test/tile/tile_id.test.cpp @@ -127,17 +127,17 @@ TEST(TileID, Canonical) { TEST(TileID, Overscaled) { EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 2, 3)); EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 2, 3)); - EXPECT_TRUE(OverscaledTileID(4, { 4, 2, 3 }) == OverscaledTileID(4, 2, 3)); - EXPECT_FALSE(OverscaledTileID(4, { 4, 2, 3 }) != OverscaledTileID(4, 2, 3)); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, { 4, 2, 3 })); - - EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, { 4, 2, 3 })); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(6, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(6, { 4, 2, 3 })); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(7, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(7, { 4, 2, 3 })); + EXPECT_TRUE(OverscaledTileID(4, 0, { 4, 2, 3 }) == OverscaledTileID(4, 2, 3)); + EXPECT_FALSE(OverscaledTileID(4, 0, { 4, 2, 3 }) != OverscaledTileID(4, 2, 3)); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 0, { 4, 2, 3 })); + + EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, 0, { 4, 2, 3 })); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(6, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(6, 0, { 4, 2, 3 })); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(7, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(7, 0, { 4, 2, 3 })); EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 2, 4)); EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 2, 4)); @@ -146,70 +146,70 @@ TEST(TileID, Overscaled) { EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, 2, 3)); EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, 2, 3)); - EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }) == OverscaledTileID(7, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }) != OverscaledTileID(7, { 4, 2, 3 })); + EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }) == OverscaledTileID(7, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }) != OverscaledTileID(7, 0, { 4, 2, 3 })); EXPECT_FALSE(OverscaledTileID(4, 2, 3) < OverscaledTileID(4, 2, 3)); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(5, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(6, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); - EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(7, { 4, 2, 3 })); - EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); - - EXPECT_EQ(8u, OverscaledTileID(7, { 4, 2, 3 }).overscaleFactor()); - EXPECT_EQ(4u, OverscaledTileID(6, { 4, 2, 3 }).overscaleFactor()); - EXPECT_EQ(2u, OverscaledTileID(5, { 4, 2, 3 }).overscaleFactor()); - EXPECT_EQ(1u, OverscaledTileID(4, { 4, 2, 3 }).overscaleFactor()); - EXPECT_EQ(2147483648u, OverscaledTileID(31, { 0, 0, 0 }).overscaleFactor()); - - EXPECT_EQ(OverscaledTileID(0, { 0, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(0)); - EXPECT_EQ(OverscaledTileID(1, { 1, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(1)); - EXPECT_EQ(OverscaledTileID(2, { 2, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(2)); - EXPECT_EQ(OverscaledTileID(3, { 3, 1, 1 }), OverscaledTileID(4, 2, 3).scaledTo(3)); - EXPECT_EQ(OverscaledTileID(4, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(4)); - EXPECT_EQ(OverscaledTileID(5, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(5)); - EXPECT_EQ(OverscaledTileID(6, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(6)); - EXPECT_EQ(OverscaledTileID(7, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(7)); - EXPECT_EQ(OverscaledTileID(8, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(8)); - EXPECT_EQ(OverscaledTileID(32, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(32)); - - EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(0)); - EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(-1)); - EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(1)); - EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(0)); - EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(-1)); - EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(1)); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(5, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(6, 0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); + EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(7,0, { 4, 2, 3 })); + EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3)); + + EXPECT_EQ(8u, OverscaledTileID(7, 0, { 4, 2, 3 }).overscaleFactor()); + EXPECT_EQ(4u, OverscaledTileID(6, 0, { 4, 2, 3 }).overscaleFactor()); + EXPECT_EQ(2u, OverscaledTileID(5, 0, { 4, 2, 3 }).overscaleFactor()); + EXPECT_EQ(1u, OverscaledTileID(4, 0, { 4, 2, 3 }).overscaleFactor()); + EXPECT_EQ(2147483648u, OverscaledTileID(31, 0, { 0, 0, 0 }).overscaleFactor()); + + EXPECT_EQ(OverscaledTileID(0, 0, { 0, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(0)); + EXPECT_EQ(OverscaledTileID(1, 0, { 1, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(1)); + EXPECT_EQ(OverscaledTileID(2, 0, { 2, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(2)); + EXPECT_EQ(OverscaledTileID(3, 0, { 3, 1, 1 }), OverscaledTileID(4, 2, 3).scaledTo(3)); + EXPECT_EQ(OverscaledTileID(4, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(4)); + EXPECT_EQ(OverscaledTileID(5, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(5)); + EXPECT_EQ(OverscaledTileID(6, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(6)); + EXPECT_EQ(OverscaledTileID(7, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(7)); + EXPECT_EQ(OverscaledTileID(8, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(8)); + EXPECT_EQ(OverscaledTileID(32, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(32)); + + EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).toUnwrapped()); + EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(4, -1, { 4, 2, 3 }).toUnwrapped()); + EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(4, 1, { 4, 2, 3 }).toUnwrapped()); + EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(5, 0, { 4, 2, 3 }).toUnwrapped()); + EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(5, -1, { 4, 2, 3 }).toUnwrapped()); + EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(5, 1, { 4, 2, 3 }).toUnwrapped()); EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(3, 1, 1))); EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(3, 1, 1))); EXPECT_TRUE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(3, 1, 1))); - EXPECT_TRUE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); - EXPECT_TRUE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); - EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); - - EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - EXPECT_TRUE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 }))); - - EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 }))); - - EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 }))); - - EXPECT_FALSE(OverscaledTileID(4, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 3, 1, 1 }))); + EXPECT_TRUE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); + EXPECT_TRUE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); + EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1))); + + EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + EXPECT_TRUE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 }))); + + EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 }))); + + EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 }))); + + EXPECT_FALSE(OverscaledTileID(4, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 3, 1, 1 }))); } TEST(TileID, Unwrapped) { @@ -273,10 +273,10 @@ TEST(TileID, Unwrapped) { EXPECT_FALSE(UnwrappedTileID(0, 1, 0) < UnwrappedTileID(1, 0, 0)); EXPECT_FALSE(UnwrappedTileID(5, 3, 6) < UnwrappedTileID(5, 3, 6)); - EXPECT_EQ(OverscaledTileID(4, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(4)); - EXPECT_EQ(OverscaledTileID(5, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(5)); - EXPECT_EQ(OverscaledTileID(6, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(6)); - EXPECT_EQ(OverscaledTileID(32, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(32)); + EXPECT_EQ(OverscaledTileID(4, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(4)); + EXPECT_EQ(OverscaledTileID(5, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(5)); + EXPECT_EQ(OverscaledTileID(6, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(6)); + EXPECT_EQ(OverscaledTileID(32, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(32)); EXPECT_EQ(UnwrappedTileID(-1, { 1, 0, 0 }), UnwrappedTileID(1, -2, 0)); EXPECT_EQ(UnwrappedTileID(-1, { 1, 0, 1 }), UnwrappedTileID(1, -2, 1)); From a0e37fe35b1af9f607600d2e792e2ff56796af6e Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 6 Jun 2017 09:01:17 -0700 Subject: [PATCH 10/15] [core] Hold on to tile yStretch value for rendering old symbolBuckets while waiting for new ones. --- src/mbgl/tile/geometry_tile.cpp | 11 +++++++++-- src/mbgl/tile/geometry_tile.hpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index ad5c2edd4c8..1ee303b50f9 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -42,7 +42,8 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, parameters.pixelRatio), glyphManager(parameters.glyphManager), imageManager(parameters.imageManager), - placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) { + placementThrottler(Milliseconds(300), [this] { invokePlacement(); }), + lastYStretch(1.0f) { } GeometryTile::~GeometryTile() { @@ -143,6 +144,9 @@ void GeometryTile::onPlacement(PlacementResult result) { if (result.iconAtlasImage) { iconAtlasImage = std::move(*result.iconAtlasImage); } + if (collisionTile.get()) { + lastYStretch = collisionTile->yStretch; + } observer->onTileChanged(*this); } @@ -265,7 +269,10 @@ void GeometryTile::querySourceFeatures( } float GeometryTile::yStretch() const { - return collisionTile->yStretch; + // collisionTile gets reset in onLayout but we don't clear the symbolBuckets + // until a new placement result comes along, so keep the yStretch value in + // case we need to render them. + return lastYStretch; } } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 3e2efe10938..943296e01b5 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -122,6 +122,7 @@ class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor { std::unique_ptr collisionTile; util::Throttler placementThrottler; + float lastYStretch; public: optional glyphAtlasTexture; From 77734cfe1b9e77a0058fa3e0db79e3c20a264165 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 29 Jun 2017 12:04:13 -0400 Subject: [PATCH 11/15] [core] fix transformMat4 It used to overwrite values in the middle of the calculation which would cause problems when `out` and `a` were a reference to the same vector. --- src/mbgl/util/mat4.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp index d3d3617b7b3..0ad0d371e5a 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 From e514138b691615be24f484986c40f486223df82a Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 16 Jun 2017 10:42:33 -0400 Subject: [PATCH 12/15] [core] improve legibility of labels that follow lines port https://github.com/mapbox/mapbox-gl-js/pull/4781 This improves legibility of labels that follow lines in pitched views. The previous approach used the limited information in the shader to calculate put the glyph in approximatelyright place. The new approach does this more accurately by doing it on the cpu where we have access to the entire line geometry. --- cmake/core-files.cmake | 2 + mapbox-gl-js | 2 +- src/mbgl/gl/context.cpp | 9 +- src/mbgl/gl/context.hpp | 13 +- src/mbgl/gl/types.hpp | 6 + src/mbgl/layout/symbol_instance.cpp | 23 +- src/mbgl/layout/symbol_instance.hpp | 9 +- src/mbgl/layout/symbol_layout.cpp | 98 +++--- src/mbgl/layout/symbol_layout.hpp | 9 +- src/mbgl/layout/symbol_projection.cpp | 266 +++++++++++++++ src/mbgl/layout/symbol_projection.hpp | 25 ++ src/mbgl/programs/attributes.hpp | 1 + src/mbgl/programs/symbol_program.cpp | 39 ++- src/mbgl/programs/symbol_program.hpp | 203 +++++------- src/mbgl/programs/uniforms.hpp | 1 - src/mbgl/renderer/buckets/symbol_bucket.cpp | 4 +- src/mbgl/renderer/buckets/symbol_bucket.hpp | 24 ++ src/mbgl/renderer/frame_history.cpp | 4 + src/mbgl/renderer/frame_history.hpp | 1 + .../renderer/layers/render_symbol_layer.cpp | 2 + .../renderer/layers/render_symbol_layer.hpp | 1 + src/mbgl/renderer/painters/painter_symbol.cpp | 29 +- src/mbgl/renderer/render_tile.cpp | 27 +- src/mbgl/renderer/render_tile.hpp | 4 +- src/mbgl/shaders/collision_box.cpp | 2 +- src/mbgl/shaders/symbol_icon.cpp | 70 ++-- src/mbgl/shaders/symbol_sdf.cpp | 208 ++++-------- src/mbgl/text/quads.cpp | 307 +++--------------- src/mbgl/text/quads.hpp | 31 +- src/mbgl/text/shaping.cpp | 14 +- test/programs/symbol_program.test.cpp | 4 - test/text/quads.test.cpp | 34 +- 32 files changed, 729 insertions(+), 743 deletions(-) create mode 100644 src/mbgl/layout/symbol_projection.cpp create mode 100644 src/mbgl/layout/symbol_projection.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index fc476fcd15f..d912392e156 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -97,6 +97,8 @@ set(MBGL_CORE_FILES src/mbgl/layout/symbol_instance.hpp src/mbgl/layout/symbol_layout.cpp src/mbgl/layout/symbol_layout.hpp + src/mbgl/layout/symbol_projection.cpp + src/mbgl/layout/symbol_projection.hpp # map include/mbgl/map/backend.hpp diff --git a/mapbox-gl-js b/mapbox-gl-js index 3dd41165546..4fa52b9e3a0 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 3dd4116554694559e3df2667ed91670438e8d1ae +Subproject commit 4fa52b9e3a0732c9a63f7d4221661a300c15c98f diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 1b4d6fbcb7e..3508b92a79d 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -164,15 +164,20 @@ 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 BufferUsageType 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(usage))); return result; } +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) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 4f5b4c797c4..f1f0ac7f8af 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -65,13 +65,19 @@ class Context : private util::noncopyable { optional> getBinaryProgram(ProgramID) const; template - VertexBuffer createVertexBuffer(VertexVector&& v) { + VertexBuffer createVertexBuffer(VertexVector&& v, const BufferUsageType usage=BufferUsageType::StaticDraw) { return VertexBuffer { v.vertexSize(), - createVertexBuffer(v.data(), v.byteSize()) + createVertexBuffer(v.data(), v.byteSize(), usage) }; } + template + void updateVertexBuffer(VertexBuffer& buffer, VertexVector&& v) { + assert(v.vertexSize() == buffer.vertexCount); + updateVertexBuffer(buffer.buffer, v.data(), v.byteSize()); + } + template IndexBuffer createIndexBuffer(IndexVector&& v) { return IndexBuffer { @@ -239,7 +245,8 @@ class Context : private util::noncopyable { State pointSize; #endif // MBGL_USE_GLES2 - UniqueBuffer createVertexBuffer(const void* data, std::size_t size); + UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsageType usage); + void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size); UniqueBuffer createIndexBuffer(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); diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 74ce67fba6a..8997fcbf31f 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -96,5 +96,11 @@ enum class UniformDataType : uint32_t { SamplerCube = 0x8B60, }; +enum class BufferUsageType : uint32_t { + StreamDraw = 0x88E0, + StaticDraw = 0x88E4, + DynamicDraw = 0x88E8, +}; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index ffb70c7ca25..02fb800df6f 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -5,8 +5,8 @@ namespace mbgl { using namespace style; -SymbolInstance::SymbolInstance(Anchor& anchor, - const GeometryCoordinates& line, +SymbolInstance::SymbolInstance(Anchor& anchor_, + GeometryCoordinates line_, const std::pair& shapedTextOrientations, optional shapedIcon, const SymbolLayoutProperties::Evaluated& layout, @@ -16,33 +16,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, + const std::array textOffset_, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, + const std::array iconOffset_, const GlyphPositionMap& positions, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : - point(anchor.point), + 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.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), + iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_), + textOffset(textOffset_), + iconOffset(iconOffset_) { // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { if (shapedIcon) { - iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first); + iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first); } if (shapedTextOrientations.first) { - auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions); + auto quads = getGlyphQuads(shapedTextOrientations.first, 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); + auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); } } diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index f199d929df9..f1df416cd19 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -13,7 +13,7 @@ class IndexedSubfeature; class SymbolInstance { public: SymbolInstance(Anchor& anchor, - const GeometryCoordinates& line, + GeometryCoordinates line, const std::pair& shapedTextOrientations, optional shapedIcon, const style::SymbolLayoutProperties::Evaluated&, @@ -23,14 +23,17 @@ class SymbolInstance { const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, + const std::array textOffset, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, + const std::array iconOffset, const GlyphPositionMap&, const IndexedSubfeature&, const std::size_t featureIndex); - Point point; + Anchor anchor; + GeometryCoordinates line; uint32_t index; bool hasText; bool hasIcon; @@ -40,6 +43,8 @@ class SymbolInstance { CollisionFeature iconCollisionFeature; WritingModeType writingModes; std::size_t featureIndex; + std::array textOffset; + std::array iconOffset; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index a6649574893..e308da618ff 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -305,6 +305,8 @@ void SymbolLayout::addFeature(const std::size_t index, const float layoutTextSize = layout.evaluate(zoom + 1, feature); const float layoutIconSize = layout.evaluate(zoom + 1, feature); + const std::array textOffset = layout.evaluate(zoom, feature); + const std::array iconOffset = layout.evaluate(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. @@ -355,8 +357,8 @@ void SymbolLayout::addFeature(const std::size_t index, symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), layoutTextSize, addToBuffers, symbolInstances.size(), - textBoxScale, textPadding, textPlacement, - iconBoxScale, iconPadding, iconPlacement, + textBoxScale, textPadding, textPlacement, textOffset, + iconBoxScale, iconPadding, iconPlacement, iconOffset, glyphPositionMap, indexedFeature, index); }; @@ -455,8 +457,8 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) 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; + 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; @@ -501,10 +503,21 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get()); if (glyphScale < collisionTile.maxScale) { + + const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI); + const bool inVerticalRange = ( + (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) || + (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4)); + const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange; + + const Range sizeData = bucket->textSizeBinder->getVertexSizeData(feature); + bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line); + for (const auto& symbol : symbolInstance.glyphQuads) { addSymbol( - bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom, - keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point); + bucket->text, sizeData, symbol, placementZoom, + keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back()); } } } @@ -513,9 +526,12 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get()); if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { + const Range sizeData = bucket->iconSizeBinder->getVertexSizeData(feature); + bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.iconOffset, placementZoom, false, symbolInstance.line); addSymbol( - bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom, - keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point); + bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom, + keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back()); } } @@ -534,15 +550,13 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) template void SymbolLayout::addSymbol(Buffer& buffer, - SymbolSizeBinder& sizeBinder, + const Range sizeData, const SymbolQuad& symbol, - const SymbolFeature& feature, const float placementZoom, const bool keepUpright, const style::SymbolPlacementType placement, - const float placementAngle, - const WritingModeType writingModes, - const Point labelAnchor) { + const Anchor& labelAnchor, + PlacedSymbol& placedSymbol) { constexpr const uint16_t vertexLength = 4; const auto &tl = symbol.tl; @@ -551,30 +565,9 @@ 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 (placement == style::SymbolPlacementType::Line && keepUpright) { + // drop incorrectly oriented glyphs + if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return; } if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits::max()) { @@ -587,20 +580,17 @@ void SymbolLayout::addSymbol(Buffer& buffer, assert(segment.vertexLength <= std::numeric_limits::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, labelAnchor, tex.x, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, labelAnchor, tex.x + tex.w, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, labelAnchor, tex.x, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, labelAnchor, 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)); - sizeBinder.populateVertexVector(feature); + auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); // add the two triangles, referencing the four coordinates we just inserted. buffer.triangles.emplace_back(index + 0, index + 1, index + 2); @@ -608,6 +598,8 @@ void SymbolLayout::addSymbol(Buffer& buffer, segment.vertexLength += vertexLength; segment.indexLength += 6; + + placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x); } void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) { @@ -647,10 +639,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& auto& segment = collisionBox.segments.back(); uint16_t index = segment.vertexLength; - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tl, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tr, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, br, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, bl, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom)); collisionBox.lines.emplace_back(index + 0, index + 1); collisionBox.lines.emplace_back(index + 1, index + 2); diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 5dc0f3eb76f..90f5b3c91d3 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -20,6 +20,7 @@ class CollisionTile; class SymbolBucket; class Anchor; class RenderLayer; +class PlacedSymbol; namespace style { class Filter; @@ -58,15 +59,13 @@ class SymbolLayout { // Adds placed items to the buffer. template void addSymbol(Buffer&, - SymbolSizeBinder& sizeBinder, + const Range sizeData, const SymbolQuad&, - const SymbolFeature& feature, float scale, const bool keepUpright, const style::SymbolPlacementType, - const float placementAngle, - WritingModeType writingModes, - const Point labelAnchor); + 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. diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp new file mode 100644 index 00000000000..99555f79971 --- /dev/null +++ b/src/mbgl/layout/symbol_projection.cpp @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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; + } + + + Point project(const Point& point, const mat4& matrix) { + vec4 pos = {{ point.x, point.y, 0, 1 }}; + matrix::transformMat4(pos, pos, matrix); + return { static_cast(pos[0] / pos[3]), static_cast(pos[1] / 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 float placementZoom, const std::array& clippingBuffer, const FrameHistory& frameHistory) { + 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 && frameHistory.isVisible(placementZoom); + } + + void addDynamicAttributes(const Point& anchorPoint, const float angle, const float placementZoom, + gl::VertexVector& dynamicVertexArray) { + auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + } + + void hideGlyphs(size_t numGlyphs, gl::VertexVector& dynamicVertexArray) { + const Point offscreenPoint = { -INFINITY, -INFINITY }; + for (size_t i = 0; i < numGlyphs; i++) { + addDynamicAttributes(offscreenPoint, 0, 25, dynamicVertexArray); + } + } + + struct PlacedGlyph { + PlacedGlyph(Point point_, float angle_) : point(point_), angle(angle_) {} + Point point; + float angle; + }; + + optional placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, + Point anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { + + 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; + + Point current = anchorPoint; + Point prev = anchorPoint; + 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(line.size())) return {}; + + prev = current; + current = project(convertPoint(line.at(currentIndex)), labelPlaneMatrix); + + distanceToPrev += currentSegmentDistance; + currentSegmentDistance = util::dist(prev, current); + } + + // The point is on the current segment. Interpolate to find it. + const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance; + const Point prevToCurrent = current - prev; + Point p = (prevToCurrent * segmentInterpolationT) + prev; + + // offset the point from the line to text-offset and icon-offset + p += util::perp(prevToCurrent) * static_cast(lineOffsetY * dir / util::mag(prevToCurrent)); + + const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x); + + return {{ p, segmentAngle }}; + } + + void placeGlyphsAlongLine(const PlacedSymbol& symbol, const float fontSize, const bool flip, const mat4& labelPlaneMatrix, + gl::VertexVector& dynamicVertexArray) { + const float fontScale = fontSize / 24.0; + const float lineOffsetX = symbol.lineOffset[0] * fontSize; + const float lineOffsetY = symbol.lineOffset[1] * fontSize; + + const Point anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix); + + std::vector placedGlyphs; + for (auto glyphOffsetX : symbol.glyphOffsets) { + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + if (placedGlyph) { + placedGlyphs.push_back(*placedGlyph); + } else { + hideGlyphs(symbol.glyphOffsets.size(), dynamicVertexArray); + return; + } + } + + for (auto& placedGlyph : placedGlyphs) { + addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray); + } + } + + void reprojectLineLabels(gl::VertexVector& dynamicVertexArray, const std::vector& placedSymbols, + const mat4& posMatrix, const style::SymbolPropertyValues& values, + const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) { + + const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom()); + + const std::array clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }}; + + const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, values.pitchAlignment == style::AlignmentType::Map, + values.rotationAlignment == style::AlignmentType::Map, state, tile.id.pixelsToTileUnits(1, state.getZoom())); + + dynamicVertexArray.clear(); + + for (auto& placedSymbol : placedSymbols) { + 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, placedSymbol.placementZoom, clippingBuffer, frameHistory)) { + hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); + continue; + } + + bool flip = false; + if (values.keepUpright) { + const Point a = project(convertPoint(placedSymbol.line.at(placedSymbol.segment)), posMatrix); + const Point b = project(convertPoint(placedSymbol.line.at(placedSymbol.segment + 1)), posMatrix); + flip = placedSymbol.useVerticalMode ? b.y > a.y : b.x < a.x; + } + + const float cameraToAnchorDistance = anchorPos[3]; + const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0); + + const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol); + const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? + fontSize * perspectiveRatio : + fontSize / perspectiveRatio; + + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, flip, labelPlaneMatrix, 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 00000000000..2652fe7ace5 --- /dev/null +++ b/src/mbgl/layout/symbol_projection.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + + class TransformState; + class RenderTile; + class FrameHistory; + class SymbolSizeBinder; + class PlacedSymbol; + namespace style { + class SymbolPropertyValues; + } // end namespace style + + 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); + + void reprojectLineLabels(gl::VertexVector&, const std::vector&, + const mat4& posMatrix, const style::SymbolPropertyValues&, + const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory); + +} // end namespace mbgl diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index f39af2deec2..684d9d60994 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -23,6 +23,7 @@ inline uint16_t packUint8Pair(T a, T b) { MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset); +MBGL_DEFINE_ATTRIBUTE(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); diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index bd43237b8f0..8790adcc63c 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -10,7 +11,7 @@ namespace mbgl { using namespace style; -static_assert(sizeof(SymbolLayoutVertex) == 20, "expected SymbolLayoutVertex size"); +static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size"); std::unique_ptr SymbolSizeBinder::create(const float tileZoom, const style::DataDrivenPropertyValue& sizeProperty, @@ -33,6 +34,7 @@ Values makeValues(const bool isText, const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state, Args&&... args) { @@ -46,21 +48,41 @@ 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; + + 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_is_text::Value{ isText }, uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, 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 }, std::forward(args)... }; @@ -71,6 +93,7 @@ SymbolIconProgram::uniformValues(const bool isText, const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state) { @@ -79,6 +102,7 @@ SymbolIconProgram::uniformValues(const bool isText, values, texsize, pixelsToGLUnits, + alongLine, tile, state ); @@ -90,25 +114,24 @@ typename SymbolSDFProgram::UniformValues SymbolSDFProgram& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state, 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::UniformValues>( isText, values, texsize, pixelsToGLUnits, + alongLine, tile, state, uniforms::u_gamma_scale::Value{ gammaScale }, - 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 130e556b467..79a961ad218 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -29,9 +29,9 @@ class RenderTile; class TransformState; namespace uniforms { -MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_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(float, u_aspect_ratio); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); @@ -40,57 +40,58 @@ 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); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< attributes::a_pos_offset, - attributes::a_label_pos, attributes::a_data> { - static Vertex vertex(Point a, + static Vertex vertex(Point labelAnchor, Point o, - Point labelAnchor, + float glyphOffsetY, uint16_t tx, uint16_t ty, - float minzoom, - float maxzoom, - float labelminzoom, - uint8_t labelangle) { + const Range& sizeData) { return Vertex { // combining pos and offset to reduce number of vertex attributes passed to shader (8 max for some devices) - {{ - static_cast(a.x), - static_cast(a.y), - static_cast(::round(o.x * 64)), // use 1/64 pixels for placement - static_cast(::round(o.y * 64)) - }}, {{ static_cast(labelAnchor.x), - static_cast(labelAnchor.y) + static_cast(labelAnchor.y), + static_cast(::round(o.x * 64)), // use 1/64 pixels for placement + static_cast(::round((o.y + glyphOffsetY) * 64)) }}, {{ tx, ty, - mbgl::attributes::packUint8Pair( - static_cast(labelminzoom * 10), // 1/10 zoom levels: z16 == 160 - static_cast(labelangle) - ), - mbgl::attributes::packUint8Pair( - static_cast(minzoom * 10), - static_cast(::fmin(maxzoom, 25) * 10) - ) + static_cast(sizeData.min * 10), + static_cast(sizeData.max * 10) }} }; } }; + +struct SymbolDynamicLayoutAttributes : gl::Attributes { + static Vertex vertex(Point anchorPoint, float labelAngle, float labelminzoom) { + return Vertex { + {{ + anchorPoint.x, + anchorPoint.y, + static_cast(mbgl::attributes::packUint8Pair( + static_cast(std::fmod(labelAngle + 2 * M_PI, 2 * M_PI) / (2 * M_PI) * 255), + static_cast(labelminzoom * 10))) + }} + }; + } +}; -class SymbolSizeAttributes : public gl::Attributes { -public: - using Attribute = attributes::a_size::Type; +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 @@ -103,18 +104,25 @@ class SymbolSizeBinder { 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 create(const float tileZoom, const style::DataDrivenPropertyValue& 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 getVertexSizeData(const GeometryTileFeature& feature) = 0; + virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0; + + 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 the smallest range of stops that covers the interval [lowerZoom, upperZoom] @@ -160,14 +168,9 @@ class ConstantSymbolSizeBinder final : public SymbolSizeBinder { ); } - SymbolSizeAttributes::Bindings attributeBindings() const override { - return SymbolSizeAttributes::Bindings { gl::DisabledAttribute() }; - } - - void upload(gl::Context&) override {} - void populateVertexVector(const GeometryTileFeature&) override {}; + Range 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) { @@ -186,14 +189,9 @@ class ConstantSymbolSizeBinder final : public SymbolSizeBinder { } 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; @@ -215,49 +213,22 @@ class SourceFunctionSymbolSizeBinder final : public SymbolSizeBinder { 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(function.evaluate(feature, defaultValue) * 10) - }} - }; - - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); + Range 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& function; const float defaultValue; - - VertexVector vertices; - optional buffer; }; class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: - using Vertex = SymbolSizeAttributes::Vertex; - using VertexVector = gl::VertexVector; - using VertexBuffer = gl::VertexBuffer; CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction& function_, const float defaultValue_) : function(function_), @@ -268,51 +239,27 @@ class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { return getCoveringStops(stops, 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(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10), - static_cast(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10), - static_cast(function.evaluate(layoutZoom, feature, defaultValue) * 10) - }} + Range 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), 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& function; const float defaultValue; float layoutZoom; Range coveringZoomStops; - - VertexVector vertices; - optional buffer; }; @@ -326,7 +273,7 @@ class SymbolProgram { using LayoutAttributes = LayoutAttrs; using LayoutVertex = typename LayoutAttributes::Vertex; - using LayoutAndSizeAttributes = gl::ConcatenateAttributes; + using LayoutAndSizeAttributes = gl::ConcatenateAttributes; using PaintProperties = PaintProps; using PaintPropertyBinders = typename PaintProperties::Binders; @@ -359,6 +306,7 @@ class SymbolProgram { gl::ColorMode colorMode, UniformValues&& uniformValues, const gl::VertexBuffer& layoutVertexBuffer, + const gl::VertexBuffer& dynamicLayoutVertexBuffer, const SymbolSizeBinder& symbolSizeBinder, const gl::IndexBuffer& indexBuffer, const gl::SegmentVector& segments, @@ -375,7 +323,7 @@ class SymbolProgram { .concat(symbolSizeBinder.uniformValues(currentZoom)) .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)), LayoutAttributes::bindings(layoutVertexBuffer) - .concat(symbolSizeBinder.attributeBindings()) + .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) .concat(paintPropertyBinders.attributeBindings(currentProperties)), indexBuffer, segments @@ -389,16 +337,17 @@ 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_collision_y_stretch, uniforms::u_camera_to_center_distance, uniforms::u_pitch, + uniforms::u_pitch_with_map, uniforms::u_max_camera_distance>, style::IconPaintProperties> { @@ -409,6 +358,7 @@ class SymbolIconProgram : public SymbolProgram< const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, + const bool alongLine, const RenderTile&, const TransformState&); }; @@ -425,21 +375,19 @@ 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_is_text, uniforms::u_collision_y_stretch, uniforms::u_camera_to_center_distance, uniforms::u_pitch, + uniforms::u_pitch_with_map, uniforms::u_max_camera_distance, uniforms::u_gamma_scale, - uniforms::u_bearing, - uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map, uniforms::u_is_halo>, PaintProperties> { @@ -449,21 +397,19 @@ 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_is_text, uniforms::u_collision_y_stretch, uniforms::u_camera_to_center_distance, uniforms::u_pitch, + uniforms::u_pitch_with_map, uniforms::u_max_camera_distance, uniforms::u_gamma_scale, - uniforms::u_bearing, - uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map, uniforms::u_is_halo>, PaintProperties>; @@ -477,6 +423,7 @@ class SymbolSDFProgram : public SymbolProgram< const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, + const bool alongLine, const RenderTile&, const TransformState&, const SymbolSDFPart); diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 861f3271c99..285d2432519 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -15,7 +15,6 @@ 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_camera_to_center_distance); MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing); MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 28e6a472503..194a5d6bd82 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -38,14 +38,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); + text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsageType::StreamDraw); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); - textSizeBinder->upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); + icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsageType::StreamDraw); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); - iconSizeBinder->upload(context); } if (!collisionBox.vertices.empty()) { diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index 002b6e28b31..ffa22e90211 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -15,6 +15,23 @@ namespace mbgl { +class PlacedSymbol { +public: + PlacedSymbol(Point anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_, + std::array lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) : + anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_), + lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {} + Point anchorPoint; + uint16_t segment; + float lowerSize; + float upperSize; + std::array lineOffset; + float placementZoom; + bool useVerticalMode; + GeometryCoordinates line; + std::vector glyphOffsets; +}; + class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, @@ -44,10 +61,13 @@ class SymbolBucket : public Bucket { struct TextBuffer { gl::VertexVector vertices; + gl::VertexVector dynamicVertices; gl::IndexVector triangles; gl::SegmentVector segments; + std::vector placedSymbols; optional> vertexBuffer; + optional> dynamicVertexBuffer; optional> indexBuffer; } text; @@ -55,11 +75,14 @@ class SymbolBucket : public Bucket { struct IconBuffer { gl::VertexVector vertices; + gl::VertexVector dynamicVertices; gl::IndexVector triangles; gl::SegmentVector segments; + std::vector placedSymbols; PremultipliedImage atlasImage; optional> vertexBuffer; + optional> dynamicVertexBuffer; optional> indexBuffer; } icon; @@ -69,6 +92,7 @@ class SymbolBucket : public Bucket { gl::SegmentVector segments; optional> vertexBuffer; + optional> dynamicVertexBuffer; optional> indexBuffer; } collisionBox; }; diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 869222b4eb9..de153b69630 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -74,4 +74,8 @@ void FrameHistory::bind(gl::Context& context, uint32_t unit) { context.bindTexture(*texture, unit); } +bool FrameHistory::isVisible(const float zoom) const { + return opacities.data[std::floor(zoom * 10)] != 0; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index f2b11f5f415..75a8b60a716 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -22,6 +22,7 @@ class FrameHistory { bool needsAnimation(const Duration&) const; void bind(gl::Context&, uint32_t); void upload(gl::Context&, uint32_t); + bool isVisible(const float zoom) const; private: std::array changeTimes; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 573e9db72ea..2fe6dd971e9 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -84,6 +84,7 @@ style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::S return style::SymbolPropertyValues { layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment layout_.get(), + layout_.get(), evaluated.get(), evaluated.get(), evaluated.get().constantOr(Color::black()).a > 0 && @@ -109,6 +110,7 @@ style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::S return style::SymbolPropertyValues { layout_.get(), layout_.get(), + layout_.get(), evaluated.get(), evaluated.get(), evaluated.get().constantOr(Color::black()).a > 0 && diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index e788336cbdd..a201b6298f5 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -40,6 +40,7 @@ class SymbolPropertyValues { // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; + bool keepUpright; // Paint std::array translate; diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index dc80f096f4d..51c3967ae70 100644 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ b/src/mbgl/renderer/painters/painter_symbol.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -52,6 +53,7 @@ void Painter::renderSymbol(PaintParameters& parameters, colorModeForRenderPass(), std::move(uniformValues), *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, *symbolSizeBinder, *buffers.indexBuffer, buffers.segments, @@ -64,10 +66,19 @@ void Painter::renderSymbol(PaintParameters& parameters, assert(dynamic_cast(&tile.tile)); GeometryTile& geometryTile = static_cast(tile.tile); + if (bucket.hasIconData()) { auto values = layer.iconPropertyValues(layout); auto paintPropertyValues = layer.iconPaintProperties(); + const bool alongLine = bucket.layout.get() == SymbolPlacementType::Line && + bucket.layout.get() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols, tile.matrix, values, tile, *(bucket.iconSizeBinder), state, frameHistory); + context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); + } + const bool iconScaled = layout.get().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; @@ -80,7 +91,7 @@ void Painter::renderSymbol(PaintParameters& parameters, if (bucket.sdfIcons) { if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), + SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, alongLine, tile, state, SymbolSDFPart::Halo), bucket.icon, bucket.iconSizeBinder, values, @@ -90,7 +101,7 @@ void Painter::renderSymbol(PaintParameters& parameters, if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), + SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, alongLine, tile, state, SymbolSDFPart::Fill), bucket.icon, bucket.iconSizeBinder, values, @@ -99,7 +110,7 @@ void Painter::renderSymbol(PaintParameters& parameters, } } else { draw(parameters.programs.symbolIcon, - SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state), + SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, alongLine, tile, state), bucket.icon, bucket.iconSizeBinder, values, @@ -114,11 +125,19 @@ void Painter::renderSymbol(PaintParameters& parameters, auto values = layer.textPropertyValues(layout); auto paintPropertyValues = layer.textPaintProperties(); + const bool alongLine = bucket.layout.get() == SymbolPlacementType::Line && + bucket.layout.get() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.text.dynamicVertices, bucket.text.placedSymbols, tile.matrix, values, tile, *(bucket.textSizeBinder), state, frameHistory); + 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, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), + SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, alongLine, tile, state, SymbolSDFPart::Halo), bucket.text, bucket.textSizeBinder, values, @@ -128,7 +147,7 @@ void Painter::renderSymbol(PaintParameters& parameters, if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), + SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, alongLine, tile, state, SymbolSDFPart::Fill), bucket.text, bucket.textSizeBinder, values, diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 59c3ea076b6..7e7e3e6d230 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace mbgl { @@ -10,24 +11,26 @@ using namespace style; mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, const std::array& 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 translate = util::rotate(Point{ 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,13 +40,13 @@ mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, mat4 RenderTile::translatedMatrix(const std::array& 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& 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) { diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index 07e2d699f73..6d374c29cb7 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -38,11 +38,11 @@ class RenderTile final { void startRender(Painter&); -private: mat4 translateVtxMatrix(const mat4& tileMatrix, const std::array& translation, style::TranslateAnchorType anchor, - const TransformState& state) const; + const TransformState& state, + const bool inViewportPixelUnits) const; }; } // namespace mbgl diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 05f306ef657..07fa94e338a 100644 --- a/src/mbgl/shaders/collision_box.cpp +++ b/src/mbgl/shaders/collision_box.cpp @@ -36,7 +36,7 @@ void main() { v_max_zoom = a_data.x; v_placement_zoom = a_data.y; - v_perspective_zoom_adjust = log2(collision_perspective_ratio * collision_adjustment) * 10.0; + v_perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); v_fade_tex = vec2((v_placement_zoom + v_perspective_zoom_adjust) / 255.0, 0.0); } diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index 8960e02c28e..cb00cdad05c 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -7,17 +7,16 @@ 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 vec2 a_label_pos; attribute vec4 a_data; +attribute vec3 a_projected_pos; -// 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 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_layout_size; // used when size is feature constant uniform highp float u_camera_to_center_distance; uniform highp float u_pitch; uniform highp float u_collision_y_stretch; @@ -31,13 +30,12 @@ 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 highp float u_zoom; -uniform bool u_rotate_with_map; -uniform vec2 u_extrude_scale; +uniform bool u_pitch_with_map; uniform vec2 u_texsize; @@ -56,61 +54,49 @@ void main() { vec2 a_offset = a_pos_offset.zw; vec2 a_tex = a_data.xy; - highp vec2 label_data = unpack_float(a_data[2]); - highp float a_labelminzoom = label_data[0]; - highp vec2 a_zoom = unpack_float(a_data[3]); - highp float a_minzoom = a_zoom[0]; - highp float a_maxzoom = a_zoom[1]; + vec2 a_size = a_data.zw; + + highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]); + highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI; + mediump float a_labelminzoom = angle_labelminzoom[1]; 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. - highp 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; } - float fontScale = u_is_text ? size / 24.0 : 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; - highp float zoomAdjust = log2(size / layoutSize); - highp float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; - // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise - highp float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); + size *= perspective_ratio; - vec4 projectedPoint = u_matrix * vec4(a_label_pos, 0, 1); - highp float camera_to_anchor_distance = projectedPoint.w; - highp float perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + float fontScale = u_is_text ? size / 24.0 : size; - vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (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); - } + highp float angle_sin = sin(segment_angle); + highp float angle_cos = cos(segment_angle); + 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; // See comments in symbol_sdf.vertex highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); - highp float perspective_zoom_adjust = log2(perspective_ratio * collision_adjustment) * 10.0; + highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); } diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index bae01a5b592..b4158bacc58 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -9,11 +9,9 @@ const char* symbol_sdf::name = "symbol_sdf"; const char* symbol_sdf::vertexSource = R"MBGL_SHADER( const float PI = 3.141592653589793; -// NOTE: the a_data attribute in this shader is manually bound (see https://github.com/mapbox/mapbox-gl-js/issues/4607). -// If removing or renaming a_data, revisit the manual binding in painter.js accordingly. attribute vec4 a_pos_offset; -attribute vec2 a_label_pos; attribute vec4 a_data; +attribute vec3 a_projected_pos; // contents of a_size vary based on the type of property value // used for {text,icon}-size. @@ -21,17 +19,14 @@ 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 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_layout_size; // used when size is feature constant -#ifdef HAS_UNIFORM_u_fill_color +#ifndef HAS_UNIFORM_u_fill_color uniform lowp float a_fill_color_t; attribute highp vec4 a_fill_color; varying highp vec4 fill_color; @@ -39,8 +34,7 @@ varying highp vec4 fill_color; uniform highp vec4 u_fill_color; #endif - -#ifdef HAS_UNIFORM_u_halo_color +#ifndef HAS_UNIFORM_u_halo_color uniform lowp float a_halo_color_t; attribute highp vec4 a_halo_color; varying highp vec4 halo_color; @@ -48,8 +42,7 @@ varying highp vec4 halo_color; uniform highp vec4 u_halo_color; #endif - -#ifdef HAS_UNIFORM_u_opacity +#ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; varying lowp float opacity; @@ -57,8 +50,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif - -#ifdef HAS_UNIFORM_u_halo_width +#ifndef HAS_UNIFORM_u_halo_width uniform lowp float a_halo_width_t; attribute lowp vec2 a_halo_width; varying lowp float halo_width; @@ -66,8 +58,7 @@ varying lowp float halo_width; uniform lowp float u_halo_width; #endif - -#ifdef HAS_UNIFORM_u_halo_blur +#ifndef HAS_UNIFORM_u_halo_blur uniform lowp float a_halo_blur_t; attribute lowp vec2 a_halo_blur; varying lowp float halo_blur; @@ -75,168 +66,100 @@ 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 highp float u_zoom; -uniform bool u_rotate_with_map; uniform bool u_pitch_with_map; uniform highp float u_pitch; -uniform highp float u_bearing; -uniform highp float u_aspect_ratio; uniform highp float u_camera_to_center_distance; -uniform highp float u_max_camera_distance; uniform highp float u_collision_y_stretch; -uniform vec2 u_extrude_scale; uniform vec2 u_texsize; -varying vec2 v_tex; -varying vec2 v_fade_tex; -varying float v_gamma_scale; -varying float v_size; - -// Used below to move the vertex out of the clip space for when the current -// zoom is out of the glyph's zoom range. -highp float clipUnusedGlyphAngles(const highp float render_size, - const highp float layout_size, - const highp float min_zoom, - const highp float max_zoom) { - highp float zoom_adjust = log2(render_size / layout_size); - highp float adjusted_zoom = (u_zoom - zoom_adjust) * 10.0; - // result: 0 if min_zoom <= adjusted_zoom < max_zoom, and 1 otherwise - return 2.0 - step(min_zoom, adjusted_zoom) - (1.0 - step(max_zoom, adjusted_zoom)); -} +varying vec4 v_data0; +varying vec2 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; + + highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]); + highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI; + mediump float a_labelminzoom = angle_labelminzoom[1]; + float size; - highp vec2 label_data = unpack_float(a_data[2]); - highp float a_labelminzoom = label_data[0]; - highp float a_lineangle = (label_data[1] / 256.0 * 2.0 * PI); - highp vec2 a_zoom = unpack_float(a_data[3]); - highp float a_minzoom = a_zoom[0]; - highp float a_maxzoom = a_zoom[1]; - - // 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. - highp float layoutSize; if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { - v_size = mix(a_size[0], a_size[1], u_size_t) / 10.0; - layoutSize = a_size[2] / 10.0; + size = mix(a_size[0], a_size[1], u_size_t) / 10.0; } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { - v_size = a_size[0] / 10.0; - layoutSize = v_size; + size = a_size[0] / 10.0; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { - v_size = u_size; - layoutSize = u_layout_size; + size = u_size; } else { - v_size = u_size; - layoutSize = u_size; + size = u_size; } - float fontScale = u_is_text ? v_size / 24.0 : v_size; - - vec4 projectedPoint = u_matrix * vec4(a_label_pos, 0, 1); + vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1); highp float camera_to_anchor_distance = projectedPoint.w; - highp float perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); - - // pitch-alignment: map - // rotation-alignment: map | viewport - if (u_pitch_with_map) { - highp float angle = u_rotate_with_map ? a_lineangle : u_bearing; - highp float asin = sin(angle); - highp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos); - vec2 offset = RotationMatrix * a_offset; - vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (offset / 64.0); - - gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += clipUnusedGlyphAngles(v_size*perspective_ratio, layoutSize, a_minzoom, a_maxzoom) * 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 - highp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); - - // 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(a_lineangle), sin(a_lineangle)), 0, 1); - highp float angle = atan((b[1] / b[3] - a[1] / a[3]) / u_aspect_ratio, b[0] / b[3] - a[0] / a[3]); - highp float asin = sin(angle); - highp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); - highp float foreshortening = (1.0 - pitchfactor) + (pitchfactor * cos(angle * 2.0)); - - vec2 offset = RotationMatrix * (vec2(foreshortening, 1.0) * a_offset); - vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (offset / 64.0); - - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - gl_Position.z += clipUnusedGlyphAngles(v_size * perspective_ratio, layoutSize, a_minzoom, a_maxzoom) * gl_Position.w; - // pitch-alignment: viewport - // rotation-alignment: viewport - } else { - vec2 extrude = fontScale * u_extrude_scale * perspective_ratio * (a_offset / 64.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - } - - gl_Position.z += - step(u_max_camera_distance * u_camera_to_center_distance, camera_to_anchor_distance) * gl_Position.w; - - v_gamma_scale = gl_Position.w / perspective_ratio; - - v_tex = a_tex / u_texsize; + // 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; + + highp float angle_sin = sin(segment_angle); + highp float angle_cos = cos(segment_angle); + 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; // incidence_stretch 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) @@ -256,8 +179,12 @@ void main() { highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); // Floor to 1/10th zoom to dodge precision issues that can cause partially hidden labels - highp float perspective_zoom_adjust = floor(log2(perspective_ratio * collision_adjustment) * 10.0); - v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); + highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); + vec2 fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); + + v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y); + v_data1 = vec2(gamma_scale, size); } )MBGL_SHADER"; @@ -273,73 +200,66 @@ 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 vec2 v_tex; -varying vec2 v_fade_tex; -varying float v_gamma_scale; -varying float v_size; +varying vec4 v_data0; +varying vec2 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 fontScale = u_is_text ? v_size / 24.0 : v_size; + float fontScale = u_is_text ? size / 24.0 : size; lowp vec4 color = fill_color; highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale); @@ -350,9 +270,9 @@ void main() { buff = (6.0 - halo_width / fontScale) / SDF_PX; } - lowp float dist = texture2D(u_texture, v_tex).a; - lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; - highp float gamma_scaled = gamma * v_gamma_scale; + 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; gl_FragColor = color * (alpha * opacity); diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index ab10c5a6b75..7908ea4abc2 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(anchor.segment) < line.size()); - const GeometryCoordinate &prev= line[anchor.segment]; - if (anchor.point.y == prev.y && anchor.point.x == prev.x && - static_cast(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(image.textureRect.h + border * 2) }; - return SymbolQuad { tl, tr, bl, br, textureRect, 0, 0, anchor.point, globalMinScale, std::numeric_limits::infinity(), shapedText.writingMode }; -} - -struct GlyphInstance { - explicit GlyphInstance(Point anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {} - explicit GlyphInstance(Point anchorPoint_, bool upsideDown_, float minScale_, float maxScale_, - float angle_) - : anchorPoint(std::move(anchorPoint_)), upsideDown(upsideDown_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} - - const Point anchorPoint; - const bool upsideDown = false; - const float minScale = globalMinScale; - const float maxScale = std::numeric_limits::infinity(); - const float angle = 0.0f; -}; - -using GlyphInstances = std::vector; - -struct VirtualSegment { - Point anchor; - Point end; - size_t index; - float minScale; - float maxScale; -}; - -inline void insertSegmentGlyph(std::back_insert_iterator 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(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 getVirtualSegmentAnchor(const Point& segmentBegin, const Point& segmentEnd, float distanceFromAnchorToSegmentBegin) { - Point segmentDirectionUnitVector = util::normal(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& segmentAnchor, - const Point& segmentEnd) { - const auto distanceFromAnchorToEnd = util::dist(segmentAnchor, segmentEnd); - return glyphDistanceFromAnchor / distanceFromAnchorToEnd; -} - -inline Point getSegmentEnd(const bool glyphIsLogicallyForward, - const GeometryCoordinates& line, - const size_t segmentIndex) { - return convertPoint(glyphIsLogicallyForward ? line[segmentIndex+1] : line[segmentIndex]); -} - -optional 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(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 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::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 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() * util::DEG2RAD; - const bool keepUpright = layout.get(); + + const float oneEm = 24.0; + std::array textOffset = layout.get(); + textOffset[0] *= oneEm; + textOffset[1] *= oneEm; SymbolQuads quads; @@ -320,67 +111,55 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const GlyphPosition& glyph = positionsIt->second; const Rect& 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 halfAdvance = glyph.metrics.advance / 2.0; + const bool alongLine = layout.get() == AlignmentType::Map && placement == SymbolPlacementType::Line; + + const Point glyphOffset = alongLine ? + Point{ positionedGlyph.x + halfAdvance, positionedGlyph.y } : + Point{ 0.0f, 0.0f }; + + const Point builtInOffset = alongLine ? + Point{ 0.0f, 0.0f } : + Point{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] }; + + + 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; - const Point center{positionedGlyph.x, static_cast(static_cast(glyph.metrics.advance) / 2.0)}; + const Point center{builtInOffset.x - halfAdvance, static_cast(static_cast(glyph.metrics.advance) / 2.0)}; - Point otl{x1, y1}; - Point otr{x2, y1}; - Point obl{x1, y2}; - Point obr{x2, y2}; + Point tl{x1, y1}; + Point tr{x2, y1}; + Point bl{x1, y2}; + Point br{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; + tl = util::rotate(tl - center, positionedGlyph.angle) + center; + tr = util::rotate(tr - center, positionedGlyph.angle) + center; + bl = util::rotate(bl - center, positionedGlyph.angle) + center; + br = util::rotate(br - center, positionedGlyph.angle) + center; } - for (const GlyphInstance &instance : glyphInstances) { - Point tl = otl; - Point tr = otr; - Point bl = obl; - Point br = obr; + if (textRotate) { + // Compute the transformation matrix. + float angle_sin = std::sin(textRotate); + float angle_cos = std::cos(textRotate); + std::array matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; - if (textRotate) { - // Compute the transformation matrix. - float angle_sin = std::sin(textRotate); - float angle_cos = std::cos(textRotate); - std::array matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; - - tl = util::matrixMultiply(matrix, tl); - tr = util::matrixMultiply(matrix, tr); - bl = util::matrixMultiply(matrix, bl); - br = util::matrixMultiply(matrix, br); - } - - // Prevent label from extending past the end of the line - const float glyphMinScale = std::max(instance.minScale, anchor.scale); - - // 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 b29f6b0ad3b..33d003c9354 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -19,50 +19,33 @@ class SymbolQuad { Point bl_, Point br_, Rect tex_, - float anchorAngle_, - float glyphAngle_, - Point anchorPoint_, - float minScale_, - float maxScale_, - WritingModeType writingMode_) + WritingModeType writingMode_, + Point 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 tl; Point tr; Point bl; Point br; Rect tex; - float anchorAngle, glyphAngle; - Point anchorPoint; - float minScale; - float maxScale; WritingModeType writingMode; + Point glyphOffset; }; using SymbolQuads = std::vector; -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 338abe2e431..c81f25d4eb5 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -27,12 +27,9 @@ void align(Shaping& shaping, const float verticalAlign, const float maxLineLength, const float lineHeight, - const std::size_t lineCount, - const Point& 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; @@ -205,7 +202,6 @@ void shapeLines(Shaping& shaping, const float horizontalAlign, const float verticalAlign, const float justify, - const Point& translate, const float verticalHeight, const WritingModeType writingMode, const Glyphs& glyphs) { @@ -259,7 +255,7 @@ void shapeLines(Shaping& shaping, } align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, - lines.size(), translate); + lines.size()); const uint32_t height = lines.size() * lineHeight; // Calculate the bounding box @@ -288,7 +284,7 @@ const Shaping getShaping(const std::u16string& logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs)); shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign, - justify, translate, verticalHeight, writingMode, glyphs); + justify, verticalHeight, writingMode, glyphs); return shaping; } diff --git a/test/programs/symbol_program.test.cpp b/test/programs/symbol_program.test.cpp index ef1e71c2698..62a2e58d7b3 100644 --- a/test/programs/symbol_program.test.cpp +++ b/test/programs/symbol_program.test.cpp @@ -10,7 +10,6 @@ TEST(SymbolProgram, SymbolSizeBinder) { EXPECT_EQ(uniformValues.get().t, true); EXPECT_EQ(uniformValues.get().t, true); EXPECT_EQ(uniformValues.get().t, 12.0f); - EXPECT_EQ(uniformValues.get().t, 12.0f); binder = SymbolSizeBinder::create(1.0f, style::CameraFunction(style::ExponentialStops({ {0.0f, 8.0f}, @@ -20,7 +19,6 @@ TEST(SymbolProgram, SymbolSizeBinder) { EXPECT_EQ(uniformValues.get().t, false); EXPECT_EQ(uniformValues.get().t, true); EXPECT_EQ(uniformValues.get().t, 9.5f); - EXPECT_EQ(uniformValues.get().t, 10.0f); binder = SymbolSizeBinder::create(0.0f, style::CameraFunction(style::ExponentialStops({ {1.0f, 8.0f}, @@ -30,7 +28,6 @@ TEST(SymbolProgram, SymbolSizeBinder) { EXPECT_EQ(uniformValues.get().t, false); EXPECT_EQ(uniformValues.get().t, true); EXPECT_EQ(uniformValues.get().t, 8.0f); - EXPECT_EQ(uniformValues.get().t, 8.0f); binder = SymbolSizeBinder::create(12.0f, style::CameraFunction(style::ExponentialStops({ {1.0f, 8.0f}, @@ -40,7 +37,6 @@ TEST(SymbolProgram, SymbolSizeBinder) { EXPECT_EQ(uniformValues.get().t, false); EXPECT_EQ(uniformValues.get().t, true); EXPECT_EQ(uniformValues.get().t, 18.0f); - EXPECT_EQ(uniformValues.get().t, 18.0f); binder = SymbolSizeBinder::create(0.0f, style::SourceFunction("x", style::ExponentialStops({ {1.0f, 8.0f}, diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp index efc3912aaac..f24b01ca875 100644 --- a/test/text/quads.test.cpp +++ b/test/text/quads.test.cpp @@ -23,10 +23,8 @@ TEST(getIconQuads, normal) { Shaping shapedText; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 16.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 16.0f, shapedText); - EXPECT_EQ(quad.anchorPoint.x, 2); - EXPECT_EQ(quad.anchorPoint.y, 3); EXPECT_EQ(quad.tl.x, -14); EXPECT_EQ(quad.tl.y, -10); EXPECT_EQ(quad.tr.x, 1); @@ -35,9 +33,6 @@ TEST(getIconQuads, normal) { EXPECT_EQ(quad.bl.y, 1); EXPECT_EQ(quad.br.x, 1); EXPECT_EQ(quad.br.y, 1); - EXPECT_EQ(quad.anchorAngle, 0.0f); - EXPECT_EQ(quad.glyphAngle, 0.0f); - EXPECT_EQ(quad.minScale, 0.5f); } TEST(getIconQuads, style) { @@ -61,10 +56,8 @@ TEST(getIconQuads, style) { { SymbolLayoutProperties::Evaluated layout; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); - EXPECT_EQ(quad.anchorPoint.x, 0); - EXPECT_EQ(quad.anchorPoint.y, 0); EXPECT_EQ(quad.tl.x, -19.5); EXPECT_EQ(quad.tl.y, -19.5); EXPECT_EQ(quad.tr.x, 0.5); @@ -73,9 +66,6 @@ TEST(getIconQuads, style) { EXPECT_EQ(quad.bl.y, 0.5); EXPECT_EQ(quad.br.x, 0.5); EXPECT_EQ(quad.br.y, 0.5); - EXPECT_EQ(quad.anchorAngle, 0.0f); - EXPECT_EQ(quad.glyphAngle, 0.0f); - EXPECT_EQ(quad.minScale, 0.5f); } // width @@ -84,7 +74,7 @@ TEST(getIconQuads, style) { layout.get() = 24.0f; layout.get() = IconTextFitType::Width; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 24.0f, shapedText); EXPECT_EQ(quad.tl.x, -60); EXPECT_EQ(quad.tl.y, 0); @@ -102,7 +92,7 @@ TEST(getIconQuads, style) { layout.get() = 12.0f; layout.get() = IconTextFitType::Width; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -30); EXPECT_EQ(quad.tl.y, -5); @@ -124,7 +114,7 @@ TEST(getIconQuads, style) { layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -40); EXPECT_EQ(quad.tl.y, -10); @@ -142,7 +132,7 @@ TEST(getIconQuads, style) { layout.get() = 24.0f; layout.get() = IconTextFitType::Height; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 24.0f, shapedText); EXPECT_EQ(quad.tl.x, -30); EXPECT_EQ(quad.tl.y, -10); @@ -160,7 +150,7 @@ TEST(getIconQuads, style) { layout.get() = 12.0f; layout.get() = IconTextFitType::Height; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -20); EXPECT_EQ(quad.tl.y, -5); @@ -182,7 +172,7 @@ TEST(getIconQuads, style) { layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -30); EXPECT_EQ(quad.tl.y, -10); @@ -200,7 +190,7 @@ TEST(getIconQuads, style) { layout.get() = 24.0f; layout.get() = IconTextFitType::Both; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 24.0f, shapedText); EXPECT_EQ(quad.tl.x, -60); EXPECT_EQ(quad.tl.y, -10); @@ -218,7 +208,7 @@ TEST(getIconQuads, style) { layout.get() = 12.0f; layout.get() = IconTextFitType::Both; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -30); EXPECT_EQ(quad.tl.y, -5); @@ -240,7 +230,7 @@ TEST(getIconQuads, style) { layout.get()[2] = 5.0f; layout.get()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -40); EXPECT_EQ(quad.tl.y, -10); @@ -262,7 +252,7 @@ TEST(getIconQuads, style) { layout.get()[2] = 10.0f; layout.get()[3] = 15.0f; SymbolQuad quad = - getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + getIconQuad(shapedIcon, layout, 12.0f, shapedText); EXPECT_EQ(quad.tl.x, -45); EXPECT_EQ(quad.tl.y, -5); From d661b4ba61a7bd7770c96464c190bfadf9a42414 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 30 Jun 2017 15:38:43 -0400 Subject: [PATCH 13/15] [core] port pitch-label collision hack https://github.com/mapbox/mapbox-gl-js/pull/4781/commits/81363951ed56c54f331ffc8d88e4e5079226a224 --- src/mbgl/text/collision_feature.cpp | 30 ++++++++++++++++++++++++----- src/mbgl/text/collision_feature.hpp | 9 +++++++-- src/mbgl/text/collision_tile.cpp | 29 ++++++++++++++++------------ src/mbgl/text/collision_tile.hpp | 2 +- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp index 58a7949a8ea..022ee50644c 100644 --- a/src/mbgl/text/collision_feature.cpp +++ b/src/mbgl/text/collision_feature.cpp @@ -42,7 +42,7 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line, bboxifyLabel(line, anchorPoint, anchor.segment, length, height); } } else { - boxes.emplace_back(anchor.point, x1, y1, x2, y2, std::numeric_limits::infinity()); + boxes.emplace_back(anchor.point, Point{ 0, 0 }, x1, y1, x2, y2, std::numeric_limits::infinity()); } } @@ -50,10 +50,10 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo const int segment, const float labelLength, const float boxSize) { const float step = boxSize / 2; const int nBoxes = std::floor(labelLength / step); - // We calculate line collision boxes out to 150% of what would normally be our + // We calculate line collision boxes 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 - const int nPitchPaddingBoxes = std::floor(nBoxes / 4); + const int nPitchPaddingBoxes = std::floor(nBoxes / 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. @@ -90,7 +90,13 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo for (int i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) { // the distance the box will be from the anchor - const float boxDistanceToAnchor = labelStartDistance + 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) @@ -143,8 +149,22 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo maxScale = std::min(maxScale, 0.99f); } - boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale); + boxes.emplace_back(boxAnchor, boxAnchor - convertPoint(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale); } } +float CollisionBox::adjustedMaxScale(const std::array& rotationMatrix, const float yStretch) const { + // When the map is pitched the distance covered by a line changes. + // Adjust the max scale by (approximatePitchedLength / approximateRegularLength) + // to compensate for this. + const Point rotatedOffset = util::matrixMultiply(rotationMatrix, offset); + const float xSqr = rotatedOffset.x * rotatedOffset.x; + const float ySqr = rotatedOffset.y * rotatedOffset.y; + const float yStretchSqr = ySqr * yStretch * yStretch; + const float adjustmentFactor = xSqr + ySqr != 0 ? + std::sqrt((xSqr + yStretchSqr) / (xSqr + ySqr)) : + 1.0f; + return maxScale * adjustmentFactor; +} + } // namespace mbgl diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp index c94ec23513f..3b6e461a26b 100644 --- a/src/mbgl/text/collision_feature.hpp +++ b/src/mbgl/text/collision_feature.hpp @@ -11,11 +11,16 @@ namespace mbgl { class CollisionBox { public: - CollisionBox(Point _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 _anchor, Point _offset, float _x1, float _y1, float _x2, float _y2, float _maxScale) : + anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {} + + float adjustedMaxScale(const std::array& rotationMatrix, const float yStretch) const; // the box is centered around the anchor point Point anchor; + + // the offset of the box from the label's anchor point + Point offset; // distances to the edges from the anchor float x1; diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index 520f66ead8d..b3fbe6f8a34 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -34,7 +34,7 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_ } -float CollisionTile::findPlacementScale(const Point& anchor, const CollisionBox& box, const Point& blockingAnchor, const CollisionBox& blocking) { +float CollisionTile::findPlacementScale(const Point& anchor, const CollisionBox& box, const float boxMaxScale, const Point& blockingAnchor, const CollisionBox& blocking) { float minPlacementScale = minScale; // Find the lowest scale at which the two boxes can fit side by side without overlapping. @@ -55,10 +55,10 @@ float CollisionTile::findPlacementScale(const Point& anchor, const Collis collisionFreeScale = blocking.maxScale; } - if (collisionFreeScale > box.maxScale) { + if (collisionFreeScale > boxMaxScale) { // 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; + collisionFreeScale = boxMaxScale; } if (collisionFreeScale > minPlacementScale && @@ -77,13 +77,13 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve static const float infinity = std::numeric_limits::infinity(); static const std::array edges {{ // left - CollisionBox(Point(0, 0), 0, -infinity, 0, infinity, infinity), + CollisionBox(Point(0, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity), // right - CollisionBox(Point(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity), + CollisionBox(Point(util::EXTENT, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity), // top - CollisionBox(Point(0, 0), -infinity, 0, infinity, 0, infinity), + CollisionBox(Point(0, 0), { 0, 0 }, -infinity, 0, infinity, 0, infinity), // bottom - CollisionBox(Point(0, util::EXTENT), -infinity, 0, infinity, 0, infinity) + CollisionBox(Point(0, util::EXTENT), { 0, 0 }, -infinity, 0, infinity, 0, infinity) }}; float minPlacementScale = minScale; @@ -91,12 +91,14 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve for (auto& box : feature.boxes) { const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor); + const float boxMaxScale = box.adjustedMaxScale(rotationMatrix, yStretch); + if (!allowOverlap) { for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) { const CollisionBox& blocking = std::get<1>(*it); Point blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor); - minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking)); + minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, boxMaxScale, blockingAnchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } @@ -107,14 +109,15 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve const Point rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 }); const Point rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 }); CollisionBox rotatedBox(box.anchor, + box.offset, 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); + boxMaxScale); for (auto& blocking : edges) { - minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking)); + minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, boxMaxScale, blocking.anchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } @@ -131,7 +134,9 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS if (minPlacementScale < maxScale) { std::vector treeBoxes; for (auto& box : feature.boxes) { - treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), box, feature.indexedFeature); + CollisionBox adjustedBox = box; + box.maxScale = box.adjustedMaxScale(rotationMatrix, yStretch); + treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), std::move(adjustedBox), feature.indexedFeature); } if (ignorePlacement) { ignoredTree.insert(treeBoxes.begin(), treeBoxes.end()); @@ -215,7 +220,7 @@ std::vector CollisionTile::queryRenderedSymbols(const Geometr // 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; + return roundedScale >= box.placementScale && roundedScale <= box.adjustedMaxScale(rotationMatrix, yStretch); }; // Check if query polygon intersects with the feature box at current scale. diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index dbff6a007b0..9868266aa2b 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -58,7 +58,7 @@ class CollisionTile { private: float findPlacementScale( - const Point& anchor, const CollisionBox& box, + const Point& anchor, const CollisionBox& box, const float boxMaxScale, const Point& blockingAnchor, const CollisionBox& blocking); Box getTreeBox(const Point& anchor, const CollisionBox& box, const float scale = 1.0); From eac297185da7c34c69c5265b6cb8c193d5537e61 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 7 Jul 2017 10:45:34 -0400 Subject: [PATCH 14/15] [core] add static asserts for more gl constants and rename BufferUsageType to BufferUsage --- src/mbgl/gl/context.cpp | 49 ++++++++++++++++++++- src/mbgl/gl/context.hpp | 4 +- src/mbgl/gl/types.hpp | 2 +- src/mbgl/renderer/buckets/symbol_bucket.cpp | 4 +- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 3508b92a79d..86ca9c06079 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -16,6 +16,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,6 +61,28 @@ static_assert(std::is_same, 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::value, "OpenGL type mismatch"); Context::Context() = default; @@ -164,7 +211,7 @@ void Context::verifyProgramLinkage(ProgramID program_) { throw std::runtime_error("program failed to link"); } -UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size, const BufferUsageType usage) { +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 } }; diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index f1f0ac7f8af..2e594618d2c 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -65,7 +65,7 @@ class Context : private util::noncopyable { optional> getBinaryProgram(ProgramID) const; template - VertexBuffer createVertexBuffer(VertexVector&& v, const BufferUsageType usage=BufferUsageType::StaticDraw) { + VertexBuffer createVertexBuffer(VertexVector&& v, const BufferUsage usage=BufferUsage::StaticDraw) { return VertexBuffer { v.vertexSize(), createVertexBuffer(v.data(), v.byteSize(), usage) @@ -245,7 +245,7 @@ class Context : private util::noncopyable { State pointSize; #endif // MBGL_USE_GLES2 - UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsageType usage); + 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); UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit); diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 8997fcbf31f..1fce878c6f4 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -96,7 +96,7 @@ enum class UniformDataType : uint32_t { SamplerCube = 0x8B60, }; -enum class BufferUsageType : uint32_t { +enum class BufferUsage : uint32_t { StreamDraw = 0x88E0, StaticDraw = 0x88E4, DynamicDraw = 0x88E8, diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 194a5d6bd82..1bd73e95dd0 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -38,13 +38,13 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); - text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsageType::StreamDraw); + text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); - icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsageType::StreamDraw); + icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); } From 4f63a39b7c0dc24f8dd1a71783ea474815116467 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 7 Jul 2017 12:25:21 -0700 Subject: [PATCH 15/15] [test] Bump mapbox-gl-js version to re-enable pitched tests --- mapbox-gl-js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 4fa52b9e3a0..c2e061bda53 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 4fa52b9e3a0732c9a63f7d4221661a300c15c98f +Subproject commit c2e061bda532ea848ed39550fe3aa4f95847cc90