From aa3a7cf02f2b5d0b99bec9d335c9681dcfa38426 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 22 May 2019 16:41:40 +0300 Subject: [core] SymbolBucket updates complete at placement stage `RenderSymbolLayer` does not have to update dynamic vertices of its buckets, this logic is moved to placement (which is already updates opacity vertices). * fixes clustering of labels when text variable placement enabled - as assignes `usesVariablePlacement` per bucket * simplifies the code in `RenderSymbolLayer` (the `RenderSymbolLayer::upload()` is now omitted). * symbol buckets are not modified after orchestration finishes --- src/mbgl/layout/symbol_projection.cpp | 12 +- src/mbgl/layout/symbol_projection.hpp | 2 +- src/mbgl/renderer/bucket.hpp | 3 +- src/mbgl/renderer/buckets/symbol_bucket.cpp | 33 +++-- src/mbgl/renderer/buckets/symbol_bucket.hpp | 5 +- src/mbgl/renderer/layers/render_symbol_layer.cpp | 157 +---------------------- src/mbgl/renderer/layers/render_symbol_layer.hpp | 2 - src/mbgl/renderer/renderer_impl.cpp | 7 +- src/mbgl/renderer/upload_parameters.hpp | 5 - src/mbgl/text/placement.cpp | 105 ++++++++++++++- src/mbgl/text/placement.hpp | 6 +- 11 files changed, 143 insertions(+), 194 deletions(-) diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index b7858f8deb..a58d90d4cf 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -361,15 +361,13 @@ namespace mbgl { void reprojectLineLabels(gfx::VertexVector>& dynamicVertexArray, const std::vector& placedSymbols, - const mat4& posMatrix, const style::SymbolPropertyValues& values, + const mat4& posMatrix, bool pitchWithMap, bool rotateWithMap, bool keepUpright, const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) { 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 bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; - const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom()); const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap, @@ -405,19 +403,19 @@ namespace mbgl { const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance()); const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol); - const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? + const float pitchScaledFontSize = pitchWithMap ? fontSize * perspectiveRatio : fontSize / perspectiveRatio; const Point anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; - PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()); + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()); useVertical = placeUnflipped == PlacementResult::UseVertical; if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical || (placeUnflipped == PlacementResult::NeedsFlipping && - placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) { + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) { hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); } } diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp index 3699eee290..58c635eaae 100644 --- a/src/mbgl/layout/symbol_projection.hpp +++ b/src/mbgl/layout/symbol_projection.hpp @@ -47,7 +47,7 @@ namespace mbgl { PointAndCameraDistance project(const Point& point, const mat4& matrix); void reprojectLineLabels(gfx::VertexVector>&, const std::vector&, - const mat4& posMatrix, const style::SymbolPropertyValues&, + const mat4& posMatrix, bool pitchWithMap, bool rotateWithMap, bool keepUpright, const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&); optional> placeFirstAndLastGlyph(const float fontScale, diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index c02e2ec341..665fc1bfa5 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -19,6 +19,7 @@ class PatternDependency; using PatternLayerMap = std::map; class Placement; class BucketPlacementParameters; +class RenderTile; class Bucket { public: @@ -59,7 +60,7 @@ public: } // Places this bucket to the given placement. Returns bucket cross-tile id on success call; `0` otherwise. virtual uint32_t place(Placement&, const BucketPlacementParameters&, std::set&) { return 0u; } - virtual void updateOpacities(Placement&, std::set&) {} + virtual void updateVertices(Placement&, bool /*updateOpacities*/, const RenderTile&, std::set&) {} protected: Bucket() = default; diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 381ef5b24b..06fccb8ffd 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -10,7 +10,7 @@ namespace mbgl { using namespace style; namespace { - std::atomic maxBucketInstanceId; +std::atomic maxBucketInstanceId; } // namespace SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_, @@ -62,7 +62,11 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) { } if (!dynamicUploaded) { - text.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(text.dynamicVertices), gfx::BufferUsageType::StreamDraw); + if (!text.dynamicVertexBuffer) { + text.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(text.dynamicVertices), gfx::BufferUsageType::StreamDraw); + } else { + uploadPass.updateVertexBuffer(*text.dynamicVertexBuffer, std::move(text.dynamicVertices)); + } } if (!placementChangesUploaded) { if (!text.opacityVertexBuffer) { @@ -84,7 +88,11 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) { uploadPass.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles)); } if (!dynamicUploaded) { - icon.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw); + if (!icon.dynamicVertexBuffer) { + icon.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw); + } else { + uploadPass.updateVertexBuffer(*icon.dynamicVertexBuffer, std::move(icon.dynamicVertices)); + } } if (!placementChangesUploaded) { if (!icon.opacityVertexBuffer) { @@ -150,11 +158,6 @@ bool SymbolBucket::hasCollisionCircleData() const { return !collisionCircle.segments.empty(); } -void SymbolBucket::updateOpacity() { - placementChangesUploaded = false; - uploaded = false; -} - void addPlacedSymbol(gfx::IndexVector& triangles, const PlacedSymbol& placedSymbol) { auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4; for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) { @@ -242,8 +245,8 @@ bool SymbolBucket::hasFormatSectionOverrides() const { } std::pair SymbolBucket::registerAtCrossTileIndex(CrossTileSymbolLayerIndex& index, const OverscaledTileID& tileID, uint32_t& maxCrossTileID) { - bool added = index.addBucket(tileID, *this, maxCrossTileID); - return std::make_pair(bucketInstanceId, added); + bool firstTimeAdded = index.addBucket(tileID, *this, maxCrossTileID); + return std::make_pair(bucketInstanceId, firstTimeAdded); } uint32_t SymbolBucket::place(Placement& placement, const BucketPlacementParameters& params, std::set& seenIds) { @@ -251,8 +254,14 @@ uint32_t SymbolBucket::place(Placement& placement, const BucketPlacementParamete return bucketInstanceId; } -void SymbolBucket::updateOpacities(Placement& placement, std::set& seenIds) { - placement.updateBucketOpacities(*this, seenIds); +void SymbolBucket::updateVertices(Placement& placement, bool updateOpacities, const RenderTile& tile, std::set& seenIds) { + if (updateOpacities) { + placement.updateBucketOpacities(*this, seenIds); + placementChangesUploaded = false; + } + placement.updateBucketDynamicVertices(*this, tile); + dynamicUploaded = false; + uploaded = false; } } // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index 3b61002890..e47672f1cd 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -60,14 +60,14 @@ public: bool hasData() const override; std::pair registerAtCrossTileIndex(CrossTileSymbolLayerIndex&, const OverscaledTileID&, uint32_t& maxCrossTileID) override; uint32_t place(Placement&, const BucketPlacementParameters&, std::set&) override; - void updateOpacities(Placement&, std::set&) override; + void updateVertices(Placement&, bool updateOpacities, const RenderTile&, std::set&) override; bool hasTextData() const; bool hasIconData() const; bool hasCollisionBoxData() const; bool hasCollisionCircleData() const; bool hasFormatSectionOverrides() const; - void updateOpacity(); + void sortFeatures(const float angle); // The result contains references to the `symbolInstances` items, sorted by viewport Y. std::vector> getSortedSymbols(const float angle); @@ -138,6 +138,7 @@ public: const float tilePixelRatio; uint32_t bucketInstanceId; bool justReloaded = false; + bool hasVariablePlacement = false; mutable optional hasFormatSectionOverrides_; std::shared_ptr> featureSortOrder; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 7fc0e08b41..dc67dea7e3 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -15,9 +15,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -29,16 +27,6 @@ namespace mbgl { using namespace style; namespace { -Point calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) { - AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor); - float shiftX = -(alignment.horizontalAlign - 0.5f) * width; - float shiftY = -(alignment.verticalAlign - 0.5f) * height; - Point offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset); - return Point( - (shiftX / textBoxScale + offset.x) * renderTextSize, - (shiftY / textBoxScale + offset.y) * renderTextSize - ); -} style::SymbolPropertyValues iconPropertyValues(const style::SymbolPaintProperties::PossiblyEvaluated& evaluated_, const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) { @@ -116,122 +104,6 @@ struct RenderableSegment { } }; -void uploadIcon(gfx::UploadPass& uploadPass, - UploadParameters& uploadParameters, - const RenderTile& tile, - const LayerRenderData& renderData) { - assert(tile.tile.kind == Tile::Kind::Geometry); - auto& bucket = static_cast(*renderData.bucket); - const auto& layout = bucket.layout; - - const bool alongLine = layout.get() != SymbolPlacementType::Point && - layout.get() == AlignmentType::Map; - - if (alongLine) { - const auto& evaluated = getEvaluated(renderData.layerProperties); - reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols, tile.matrix, - iconPropertyValues(evaluated, layout), tile, *bucket.iconSizeBinder, - uploadParameters.state); - - uploadPass.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, - std::move(bucket.icon.dynamicVertices)); - } -} - -void uploadText(gfx::UploadPass& uploadPass, - UploadParameters& uploadParameters, - const RenderTile& tile, - const LayerRenderData& renderData, - bool& hasVariablePlacement) { - assert(tile.tile.kind == Tile::Kind::Geometry); - auto& bucket = static_cast(*renderData.bucket); - const auto& layout = bucket.layout; - - const bool alongLine = layout.get() != SymbolPlacementType::Point && - layout.get() == AlignmentType::Map; - - if (alongLine) { - const auto& evaluated = getEvaluated(renderData.layerProperties); - reprojectLineLabels(bucket.text.dynamicVertices, - bucket.text.placedSymbols, - tile.matrix, - textPropertyValues(evaluated, layout), - tile, - *bucket.textSizeBinder, - uploadParameters.state); - - uploadPass.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); - } else if (!layout.get().empty()) { - bucket.text.dynamicVertices.clear(); - - hasVariablePlacement = false; - - const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(uploadParameters.state.getZoom()); - const float tileScale = std::pow(2, uploadParameters.state.getZoom() - tile.tile.id.overscaledZ); - const bool rotateWithMap = layout.get() == AlignmentType::Map; - const bool pitchWithMap = layout.get() == AlignmentType::Map; - const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, uploadParameters.state.getZoom()); - const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, uploadParameters.state, pixelsToTileUnits); - - for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { - optional variableOffset; - if (!symbol.hidden && symbol.crossTileID != 0u) { - auto it = uploadParameters.variableOffsets.find(symbol.crossTileID); - if (it != uploadParameters.variableOffsets.end()) { - hasVariablePlacement |= true; - variableOffset = it->second; - } - } - - if (!variableOffset) { - // These symbols are from a justification that is not being used, or a label that wasn't placed - // so we don't need to do the extra math to figure out what incremental shift to apply. - hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices); - } else { - const Point tileAnchor = symbol.anchorPoint; - const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix); - const float perspectiveRatio = 0.5f + 0.5f * (uploadParameters.state.getCameraToCenterDistance() / projectedAnchor.second); - float renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol) * perspectiveRatio / util::ONE_EM; - if (pitchWithMap) { - // Go from size in pixels to equivalent size in tile units - renderTextSize *= bucket.tilePixelRatio / tileScale; - } - - auto shift = calculateVariableRenderShift( - (*variableOffset).anchor, - (*variableOffset).width, - (*variableOffset).height, - (*variableOffset).radialOffset, - (*variableOffset).textBoxScale, - renderTextSize); - - // Usual case is that we take the projected anchor and add the pixel-based shift - // calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent - // tile-unit based shift to the anchor before projecting to the label plane. - Point shiftedAnchor; - if (pitchWithMap) { - shiftedAnchor = project(Point(tileAnchor.x + shift.x, tileAnchor.y + shift.y), - labelPlaneMatrix).first; - } else { - if (rotateWithMap) { - auto rotated = util::rotate(shift, -uploadParameters.state.getPitch()); - shiftedAnchor = Point(projectedAnchor.first.x + rotated.x, - projectedAnchor.first.y + rotated.y); - } else { - shiftedAnchor = Point(projectedAnchor.first.x + shift.x, - projectedAnchor.first.y + shift.y); - } - } - - for (std::size_t i = 0; i < symbol.glyphOffsets.size(); i++) { - addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices); - } - } - } - uploadPass.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); - } -} - template void drawIcon(const DrawFn& draw, const RenderTile& tile, @@ -314,8 +186,7 @@ void drawText(const DrawFn& draw, const LayerRenderData& renderData, SegmentsWrapper textSegments, const SymbolBucket::PaintProperties& bucketPaintProperties, - const PaintParameters& parameters, - bool hasVariablePlacement) { + const PaintParameters& parameters) { assert(tile.tile.kind == Tile::Kind::Geometry); auto& geometryTile = static_cast(tile.tile); auto& bucket = static_cast(*renderData.bucket); @@ -335,7 +206,7 @@ void drawText(const DrawFn& draw, if (values.hasHalo) { draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + SymbolSDFTextProgram::layoutUniformValues(true, bucket.hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), bucket.text, textSegments, bucket.textSizeBinder, @@ -350,7 +221,7 @@ void drawText(const DrawFn& draw, if (values.hasFill) { draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + SymbolSDFTextProgram::layoutUniformValues(true, bucket.hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), bucket.text, textSegments, bucket.textSizeBinder, @@ -413,24 +284,6 @@ bool RenderSymbolLayer::hasCrossfade() const { return false; } -void RenderSymbolLayer::upload(gfx::UploadPass& uploadPass, UploadParameters& uploadParameters) { - for (const RenderTile& tile : renderTiles) { - const LayerRenderData* renderData = tile.tile.getLayerRenderData(*baseImpl); - if (!renderData) { - continue; - } - - auto& bucket = static_cast(*renderData->bucket); - if (bucket.hasIconData()) { - uploadIcon(uploadPass, uploadParameters, tile, *renderData); - } - - if (bucket.hasTextData()) { - uploadText(uploadPass, uploadParameters, tile, *renderData, hasVariablePlacement); - } - } -} - void RenderSymbolLayer::render(PaintParameters& parameters) { if (parameters.pass == RenderPass::Opaque) { return; @@ -537,7 +390,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters) { if (sortFeaturesByKey) { addRenderables(bucket.text.segments, true /*isText*/); } else { - drawText(draw, tile, *renderData, std::ref(bucket.text.segments), bucketPaintProperties, parameters, hasVariablePlacement); + drawText(draw, tile, *renderData, std::ref(bucket.text.segments), bucketPaintProperties, parameters); } } @@ -619,7 +472,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters) { if (sortFeaturesByKey) { for (auto& renderable : renderableSegments) { if (renderable.isText) { - drawText(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters, hasVariablePlacement); + drawText(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters); } else { drawIcon(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters); } diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index ffd05aa310..d9ce8c688d 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -65,7 +65,6 @@ private: void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; bool hasCrossfade() const override; - void upload(gfx::UploadPass&, UploadParameters&) override; void render(PaintParameters&) override; void prepare(const LayerPrepareParameters&) override; @@ -76,7 +75,6 @@ private: float textSize = 16.0f; bool hasFormatSectionOverrides = false; - bool hasVariablePlacement = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index f8e6b65f7f..a809fd843a 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -345,10 +345,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { placement->setStale(); } - if (placementChanged || symbolBucketsChanged) { - for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) { - placement->updateLayerOpacities(*it); - } + for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) { + placement->updateLayerBuckets(*it, placementChanged || symbolBucketsChanged); } } @@ -385,7 +383,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { UploadParameters uploadParameters{ updateParameters.transformState, - placement->getVariableOffsets(), *imageManager, *lineAtlas, }; diff --git a/src/mbgl/renderer/upload_parameters.hpp b/src/mbgl/renderer/upload_parameters.hpp index 0328309927..14da1026c6 100644 --- a/src/mbgl/renderer/upload_parameters.hpp +++ b/src/mbgl/renderer/upload_parameters.hpp @@ -7,23 +7,18 @@ namespace mbgl { class TransformState; class LineAtlas; -class VariableOffset; -using VariableOffsets = std::unordered_map; class UploadParameters { public: UploadParameters(const TransformState& state_, - const VariableOffsets& variableOffsets_, ImageManager& imageManager_, LineAtlas& lineAtlas_) : state(state_), - variableOffsets(variableOffsets_), imageManager(imageManager_), lineAtlas(lineAtlas_) { } const TransformState& state; - const VariableOffsets& variableOffsets; ImageManager& imageManager; LineAtlas& lineAtlas; }; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 49a57ae7c0..8dbb7fe346 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -396,10 +396,110 @@ void Placement::commit(TimePoint now) { fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime; } -void Placement::updateLayerOpacities(const RenderLayer& layer) { +void Placement::updateLayerBuckets(const RenderLayer& layer, bool updateOpacities) { std::set seenCrossTileIDs; for (const auto& item : layer.getPlacementData()) { - item.bucket.get().updateOpacities(*this, seenCrossTileIDs); + item.bucket.get().updateVertices(*this, updateOpacities, item.tile, seenCrossTileIDs); + } +} + +namespace { +Point calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) { + AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor); + float shiftX = -(alignment.horizontalAlign - 0.5f) * width; + float shiftY = -(alignment.verticalAlign - 0.5f) * height; + Point offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset); + return { (shiftX / textBoxScale + offset.x) * renderTextSize, + (shiftY / textBoxScale + offset.y) * renderTextSize }; +} +} // namespace + +void Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const RenderTile& tile) { + using namespace style; + const auto& layout = bucket.layout; + const bool alongLine = layout.get() != SymbolPlacementType::Point; + if (alongLine) { + if (bucket.hasIconData() && layout.get() == AlignmentType::Map) { + const bool pitchWithMap = layout.get() == style::AlignmentType::Map; + const bool keepUpright = layout.get(); + reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols, + tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright, + tile, *bucket.iconSizeBinder, state); + } + + if (bucket.hasTextData() && layout.get() == AlignmentType::Map) { + const bool pitchWithMap = layout.get() == style::AlignmentType::Map; + const bool keepUpright = layout.get(); + reprojectLineLabels(bucket.text.dynamicVertices, bucket.text.placedSymbols, + tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright, + tile, *bucket.textSizeBinder, state); + } + } else if (!layout.get().empty() && bucket.hasTextData()) { + bucket.text.dynamicVertices.clear(); + bucket.hasVariablePlacement = false; + + const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom()); + const float tileScale = std::pow(2, state.getZoom() - tile.tile.id.overscaledZ); + const bool rotateWithMap = layout.get() == AlignmentType::Map; + const bool pitchWithMap = layout.get() == AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom()); + const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); + + for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { + optional variableOffset; + if (!symbol.hidden && symbol.crossTileID != 0u) { + auto it = variableOffsets.find(symbol.crossTileID); + if (it != variableOffsets.end()) { + bucket.hasVariablePlacement = true; + variableOffset = it->second; + } + } + + if (!variableOffset) { + // These symbols are from a justification that is not being used, or a label that wasn't placed + // so we don't need to do the extra math to figure out what incremental shift to apply. + hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices); + } else { + const Point tileAnchor = symbol.anchorPoint; + const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix); + const float perspectiveRatio = 0.5f + 0.5f * (state.getCameraToCenterDistance() / projectedAnchor.second); + float renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol) * perspectiveRatio / util::ONE_EM; + if (pitchWithMap) { + // Go from size in pixels to equivalent size in tile units + renderTextSize *= bucket.tilePixelRatio / tileScale; + } + + auto shift = calculateVariableRenderShift( + (*variableOffset).anchor, + (*variableOffset).width, + (*variableOffset).height, + (*variableOffset).radialOffset, + (*variableOffset).textBoxScale, + renderTextSize); + + // Usual case is that we take the projected anchor and add the pixel-based shift + // calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent + // tile-unit based shift to the anchor before projecting to the label plane. + Point shiftedAnchor; + if (pitchWithMap) { + shiftedAnchor = project(Point(tileAnchor.x + shift.x, tileAnchor.y + shift.y), + labelPlaneMatrix).first; + } else { + if (rotateWithMap) { + auto rotated = util::rotate(shift, -state.getPitch()); + shiftedAnchor = Point(projectedAnchor.first.x + rotated.x, + projectedAnchor.first.y + rotated.y); + } else { + shiftedAnchor = Point(projectedAnchor.first.x + shift.x, + projectedAnchor.first.y + shift.y); + } + } + + for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) { + addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices); + } + } + } } } @@ -559,7 +659,6 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& } } - bucket.updateOpacity(); bucket.sortFeatures(state.getBearing()); auto retainedData = retainedQueryData.find(bucket.bucketInstanceId); if (retainedData != retainedQueryData.end()) { diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index e0fcac3350..c159286a2b 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -105,7 +105,7 @@ public: Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, std::unique_ptr prevPlacementOrNull = nullptr); void placeLayer(const RenderLayer&, const mat4&, bool showCollisionBoxes); void commit(TimePoint); - void updateLayerOpacities(const RenderLayer&); + void updateLayerBuckets(const RenderLayer&, bool updateOpacities); float symbolFadeChange(TimePoint now) const; bool hasTransitions(TimePoint now) const; @@ -116,9 +116,6 @@ public: void setStale(); const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const; - using VariableOffsets = std::unordered_map; - const VariableOffsets& getVariableOffsets() const { return variableOffsets; } - private: friend SymbolBucket; void placeLayerBucket( @@ -126,6 +123,7 @@ private: const BucketPlacementParameters&, std::set& seenCrossTileIDs); + void updateBucketDynamicVertices(SymbolBucket& bucket, const RenderTile& tile); void updateBucketOpacities(SymbolBucket&, std::set&); void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&); -- cgit v1.2.1