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..c2e061bda53 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 3dd4116554694559e3df2667ed91670438e8d1ae +Subproject commit c2e061bda532ea848ed39550fe3aa4f95847cc90 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/gl/context.cpp b/src/mbgl/gl/context.cpp index 1b4d6fbcb7e..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,15 +211,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 BufferUsage usage) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); UniqueBuffer result { std::move(id), { this } }; vertexBuffer = result; - MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast(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..2e594618d2c 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 BufferUsage usage=BufferUsage::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 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); 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..1fce878c6f4 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 BufferUsage : 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 adc7eaaed89..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); + 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); + bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom, + keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back()); } } @@ -534,14 +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 Anchor& labelAnchor, + PlacedSymbol& placedSymbol) { constexpr const uint16_t vertexLength = 4; const auto &tl = symbol.tl; @@ -550,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()) { @@ -586,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, tex.x, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData)); - 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); @@ -607,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) { @@ -646,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, 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.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 4ee52e843fb..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,14 +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 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/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/attributes.hpp b/src/mbgl/programs/attributes.hpp index 8f2751080f4..684d9d60994 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -23,6 +23,9 @@ 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); 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..ba99e0c0875 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,26 @@ 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, + uniforms::u_fadetexture>, 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 cdbd6b9713a..8790adcc63c 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -32,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) { @@ -45,18 +48,42 @@ 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)... }; } @@ -66,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) { @@ -74,6 +102,7 @@ SymbolIconProgram::uniformValues(const bool isText, values, texsize, pixelsToGLUnits, + alongLine, tile, state ); @@ -85,26 +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_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index c11e0b5ca14..79a961ad218 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -29,10 +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(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); @@ -41,50 +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_data> { - static Vertex vertex(Point a, + static Vertex vertex(Point labelAnchor, Point o, + 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(labelAnchor.x), + static_cast(labelAnchor.y), static_cast(::round(o.x * 64)), // use 1/64 pixels for placement - static_cast(::round(o.y * 64)) + 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 @@ -97,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] @@ -154,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) { @@ -180,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; @@ -209,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_), @@ -262,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; }; @@ -320,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; @@ -353,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, @@ -369,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 @@ -383,13 +337,18 @@ 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_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> { public: @@ -399,6 +358,7 @@ class SymbolIconProgram : public SymbolProgram< const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, + const bool alongLine, const RenderTile&, const TransformState&); }; @@ -415,18 +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_gamma_scale, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, - uniforms::u_bearing, - uniforms::u_aspect_ratio, uniforms::u_pitch_with_map, + uniforms::u_max_camera_distance, + uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties> { @@ -436,18 +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_gamma_scale, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, - uniforms::u_bearing, - uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map, + uniforms::u_pitch_with_map, + uniforms::u_max_camera_distance, + uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties>; @@ -461,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 c8f8684ba17..285d2432519 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -14,6 +14,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(Color, u_color); MBGL_DEFINE_UNIFORM_SCALAR(float, u_blur); MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_collision_y_stretch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing); MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); @@ -49,6 +50,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/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 28e6a472503..1bd73e95dd0 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::BufferUsage::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::BufferUsage::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 6540fc96120..2fe6dd971e9 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -84,23 +84,39 @@ 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 && 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(), + layout_.get(), evaluated.get(), 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..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; @@ -47,6 +48,8 @@ class SymbolPropertyValues { bool hasHalo; bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else }; } // namespace style diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index d3a505aa3f6..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, @@ -145,13 +164,17 @@ 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 }, 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() }, + uniforms::u_fadetexture::Value{ 1 } }, *bucket.collisionBox.vertexBuffer, *bucket.collisionBox.indexBuffer, 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/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index a8e6c128ba6..5b1e6217438 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -175,11 +175,13 @@ 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) { + const PlacementConfig config { parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.transformState.getCameraToCenterDistance(), + parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()), + parameters.debugOptions & MapDebugOptions::Collision }; + pair.second->setPlacementConfig(config); } } diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 5f733c6a1e4..07fa94e338a 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 = floor(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..cb00cdad05c 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -7,17 +7,19 @@ namespace shaders { const char* symbol_icon::name = "symbol_icon"; const char* symbol_icon::vertexSource = R"MBGL_SHADER( +const float PI = 3.141592653589793; attribute vec4 a_pos_offset; attribute vec4 a_data; +attribute vec3 a_projected_pos; -// icon-size data (see symbol_sdf.vertex.glsl for more) -attribute vec3 a_size; uniform bool u_is_size_zoom_constant; uniform bool u_is_size_feature_constant; -uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function -uniform mediump float u_size; // used when size is both zoom and feature constant -uniform mediump float u_layout_size; // used when size is feature constant +uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform highp float u_size; // used when size is both zoom and feature constant +uniform highp float u_camera_to_center_distance; +uniform highp float u_pitch; +uniform highp float u_collision_y_stretch; #ifndef HAS_UNIFORM_u_opacity @@ -28,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 mediump float u_zoom; -uniform bool u_rotate_with_map; -uniform vec2 u_extrude_scale; +uniform bool u_pitch_with_map; uniform vec2 u_texsize; @@ -53,53 +54,50 @@ 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]; + 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. - mediump float layoutSize; if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { size = mix(a_size[0], a_size[1], u_size_t) / 10.0; - layoutSize = a_size[2] / 10.0; } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { size = a_size[0] / 10.0; - layoutSize = size; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { size = u_size; - layoutSize = u_layout_size; } else { size = u_size; - layoutSize = u_size; } + vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + // See comments in symbol_sdf.vertex + highp float distance_ratio = u_pitch_with_map ? + camera_to_anchor_distance / u_camera_to_center_distance : + u_camera_to_center_distance / camera_to_anchor_distance; + highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + + size *= perspective_ratio; + float fontScale = u_is_text ? size / 24.0 : size; - mediump float zoomAdjust = log2(size / layoutSize); - mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; - // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise - mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); + highp float 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); - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); - if (u_rotate_with_map) { - gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += z * gl_Position.w; - } else { - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - } + vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); v_tex = a_tex / u_texsize; - v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); + // 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 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); } )MBGL_SHADER"; diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index cce6b769a67..b4158bacc58 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -11,6 +11,7 @@ const float PI = 3.141592653589793; attribute vec4 a_pos_offset; 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. @@ -18,14 +19,11 @@ attribute vec4 a_data; // For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature. // For composite functions: // [ text-size(lowerZoomStop, feature), -// text-size(upperZoomStop, feature), -// layoutSize == text-size(layoutZoomLevel, feature) ] -attribute vec3 a_size; +// text-size(upperZoomStop, feature) ] uniform bool u_is_size_zoom_constant; uniform bool u_is_size_feature_constant; -uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function -uniform mediump float u_size; // used when size is both zoom and feature constant -uniform mediump float u_layout_size; // used when size is feature constant +uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform highp float u_size; // used when size is both zoom and feature constant #ifndef HAS_UNIFORM_u_fill_color @@ -68,17 +66,15 @@ varying lowp float halo_blur; uniform lowp float u_halo_blur; #endif -// matrix is for the vertex position. uniform mat4 u_matrix; +uniform mat4 u_label_plane_matrix; +uniform mat4 u_gl_coord_matrix; uniform bool u_is_text; -uniform mediump float u_zoom; -uniform bool u_rotate_with_map; uniform bool u_pitch_with_map; -uniform mediump float u_pitch; -uniform mediump float u_bearing; -uniform mediump float u_aspect_ratio; -uniform vec2 u_extrude_scale; +uniform highp float u_pitch; +uniform highp float u_camera_to_center_distance; +uniform highp float u_collision_y_stretch; uniform vec2 u_texsize; @@ -121,94 +117,71 @@ void main() { vec2 a_offset = a_pos_offset.zw; vec2 a_tex = a_data.xy; + vec2 a_size = a_data.zw; - mediump vec2 label_data = unpack_float(a_data[2]); - mediump float a_labelminzoom = label_data[0]; - mediump float a_labelangle = label_data[1]; - - mediump vec2 a_zoom = unpack_float(a_data[3]); - mediump float a_minzoom = a_zoom[0]; - mediump float a_maxzoom = a_zoom[1]; + highp 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. - mediump float layoutSize; if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { size = mix(a_size[0], a_size[1], u_size_t) / 10.0; - layoutSize = a_size[2] / 10.0; } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { size = a_size[0] / 10.0; - layoutSize = size; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { size = u_size; - layoutSize = u_layout_size; } else { size = u_size; - layoutSize = u_size; } + vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + // If the label is pitched with the map, layout is done in pitched space, + // which makes labels in the distance smaller relative to viewport space. + // We counteract part of that effect by multiplying by the perspective ratio. + // If the label isn't pitched with the map, we do layout in viewport space, + // which makes labels in the distance larger relative to the features around + // them. We counteract part of that effect by dividing by the perspective ratio. + highp float distance_ratio = u_pitch_with_map ? + camera_to_anchor_distance / u_camera_to_center_distance : + u_camera_to_center_distance / camera_to_anchor_distance; + highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + + size *= perspective_ratio; + float fontScale = u_is_text ? size / 24.0 : size; - mediump float zoomAdjust = log2(size / layoutSize); - mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; - // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise - // Used below to move the vertex out of the clip space for when the current - // zoom is out of the glyph's zoom range. - mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); - - // pitch-alignment: map - // rotation-alignment: map | viewport - if (u_pitch_with_map) { - lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing; - lowp float asin = sin(angle); - lowp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos); - vec2 offset = RotationMatrix * a_offset; - vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); - gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += z * gl_Position.w; - // pitch-alignment: viewport - // rotation-alignment: map - } else if (u_rotate_with_map) { - // foreshortening factor to apply on pitched maps - // as a label goes from horizontal <=> vertical in angle - // it goes from 0% foreshortening to up to around 70% foreshortening - lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); - - lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI; - - // use the lineangle to position points a,b along the line - // project the points and calculate the label angle in projected space - // this calculation allows labels to be rendered unskewed on pitched maps - vec4 a = u_matrix * vec4(a_pos, 0, 1); - vec4 b = u_matrix * vec4(a_pos + vec2(cos(lineangle),sin(lineangle)), 0, 1); - lowp float angle = atan((b[1]/b[3] - a[1]/a[3])/u_aspect_ratio, b[0]/b[3] - a[0]/a[3]); - lowp float asin = sin(angle); - lowp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); - - vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset); - vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - gl_Position.z += z * gl_Position.w; - // pitch-alignment: viewport - // rotation-alignment: viewport - } else { - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - } + highp float 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; - vec2 fade_tex = vec2(a_labelminzoom / 255.0, 0.0); + // 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 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); diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp index 71d7cc74e06..022ee50644c 100644 --- a/src/mbgl/text/collision_feature.cpp +++ b/src/mbgl/text/collision_feature.cpp @@ -42,14 +42,18 @@ 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()); } } 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 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 / 2); // offset the center of the first box by half a box so that the edge of the // box is at the edge of the label. @@ -58,24 +62,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo GeometryCoordinate &p = anchorPoint; int index = segment + 1; float anchorDistance = firstBoxOffset; + const float labelStartDistance = -labelLength / 2; + const float paddingStartDistance = labelStartDistance - labelLength / 8; // move backwards along the line to the first segment the label appears on do { index--; - // there isn't enough room for the label after the beginning of the line - // checkMaxAngle should have already caught this - if (index < 0) return; + if (index < 0) { + if (anchorDistance > labelStartDistance) { + // there isn't enough room for the label after the beginning of the line + // checkMaxAngle should have already caught this + return; + } else { + // The line doesn't extend far enough back for all of our padding, + // but we got far enough to show the label under most conditions. + index = 0; + break; + } + } anchorDistance -= util::dist(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 boxOffset = i * step; + float boxDistanceToAnchor = labelStartDistance + boxOffset; + + // make the distance between pitch padding boxes bigger + if (boxOffset < 0) boxDistanceToAnchor += boxOffset; + if (boxOffset > labelLength) boxDistanceToAnchor += boxOffset - labelLength; + + if (boxDistanceToAnchor < anchorDistance) { + // The line doesn't extend far enough back for this box, skip it + // (This could allow for line collisions on distant tiles) + continue; + } // the box is not on the current segment. Move to the next segment. while (anchorDistance + segmentLength < boxDistanceToAnchor) { @@ -99,11 +125,46 @@ 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); + 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 368750c89f3..b3fbe6f8a34 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -20,16 +20,21 @@ 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))); } -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. @@ -50,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 && @@ -72,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; @@ -86,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; } } @@ -102,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; } } @@ -126,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()); @@ -157,13 +167,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,23 +208,30 @@ 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 { 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. 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..9868266aa2b 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; @@ -58,12 +58,14 @@ 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); 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..1e1279341d3 100644 --- a/src/mbgl/text/placement_config.hpp +++ b/src/mbgl/text/placement_config.hpp @@ -1,15 +1,21 @@ #pragma once +#include + 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 && + (pitch * util::RAD2DEG < 25 || cameraToTileDistance == rhs.cameraToTileDistance) && + debug == rhs.debug; } bool operator!=(const PlacementConfig& rhs) const { @@ -19,6 +25,8 @@ class PlacementConfig { public: float angle; float pitch; + float cameraToCenterDistance; + float cameraToTileDistance; bool debug; }; 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/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 4ab11d79fed..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); } @@ -264,4 +268,11 @@ void GeometryTile::querySourceFeatures( } } +float GeometryTile::yStretch() const { + // 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 77202d20b6d..943296e01b5 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(); @@ -120,6 +122,7 @@ class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor { std::unique_ptr collisionTile; util::Throttler placementThrottler; + float lastYStretch; public: optional glyphAtlasTexture; 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; 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/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 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/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); 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));