diff options
author | Alexander Shalamov <alexander.shalamov@mapbox.com> | 2019-04-10 11:57:11 +0300 |
---|---|---|
committer | Alexander Shalamov <alexander.shalamov@mapbox.com> | 2019-04-11 15:13:38 +0300 |
commit | fa37b21d8fd13e5f5afdb4062d0b9f2aa3f681e1 (patch) | |
tree | b85d5390087eee72c03e55750fc3b12f35742f03 | |
parent | 5abeb3d887feed8f7fd53f285f4e092672aefec7 (diff) | |
download | qtlocation-mapboxgl-fa37b21d8fd13e5f5afdb4062d0b9f2aa3f681e1.tar.gz |
[core] Sort cross-tile symbol segments using symbol-sort-key
-rw-r--r-- | src/mbgl/programs/symbol_program.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_symbol_layer.cpp | 402 |
2 files changed, 229 insertions, 179 deletions
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 56477166d8..980ca253b9 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -298,7 +298,7 @@ public: return allAttributeBindings.activeCount(); } - template <class DrawMode> + template <class DrawMode, class SegmentsContainer> void draw(gfx::Context& context, gfx::RenderPass& renderPass, const DrawMode& drawMode, @@ -307,7 +307,7 @@ public: const gfx::ColorMode& colorMode, const gfx::CullFaceMode& cullFaceMode, const gfx::IndexBuffer& indexBuffer, - const SegmentVector<AttributeList>& segments, + const SegmentsContainer& segments, const UniformValues& uniformValues, const AttributeBindings& allAttributeBindings, const TextureBindings& textureBindings, @@ -318,7 +318,7 @@ public: return; } - for (auto& segment : segments) { + for (const Segment<AttributeList>& segment : segments) { auto drawScopeIt = segment.drawScopes.find(layerID); if (drawScopeIt == segment.drawScopes.end()) { diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 17bb08c0ea..90f09b4d15 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -23,7 +23,6 @@ namespace mbgl { using namespace style; - namespace { Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) { AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor); @@ -35,6 +34,14 @@ Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float (shiftY / textBoxScale + offset.y) * renderTextSize ); } + +struct RenderableSegment { + using SegmentRefVector = std::vector<std::reference_wrapper<const Segment<SymbolTextAttributes>>>; + SegmentRefVector segments; + std::function<void(const SegmentRefVector&)> drawSegments; + unsigned int sortKey; +}; + } // namespace RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl) @@ -92,6 +99,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { return; } + const bool sortFeaturesByKey = !impl().layout.get<SymbolSortKey>().isUndefined(); + std::vector<RenderableSegment> renderableSegments; + renderableSegments.reserve(renderTiles.size()); + for (const RenderTile& tile : renderTiles) { auto bucket_ = tile.tile.getBucket<SymbolBucket>(*baseImpl); if (!bucket_) { @@ -103,14 +114,16 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { const auto& evaluated_ = bucketPaintProperties.evaluated; const auto& layout = bucket.layout; - auto draw = [&] (auto& programInstance, - auto&& uniformValues, - const auto& buffers, - const auto& symbolSizeBinder, - const SymbolPropertyValues& values_, - const auto& binders, - const auto& paintProperties, - auto&& textureBindings) { + const auto draw = [¶meters, this] (auto& programInstance, + auto&& uniformValues, + const auto& buffers, + const auto& segments, + const auto& symbolSizeBinder, + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties, + auto&& textureBindings) { + const auto allUniformValues = programInstance.computeAllUniformValues( std::move(uniformValues), *symbolSizeBinder, @@ -118,6 +131,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { paintProperties, parameters.state.getZoom() ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *buffers.vertexBuffer, *buffers.dynamicVertexBuffer, @@ -126,7 +140,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { paintProperties ); - checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + this->checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw( parameters.context, @@ -139,11 +153,11 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { parameters.colorModeForRenderPass(), gfx::CullFaceMode::disabled(), *buffers.indexBuffer, - buffers.segments, + segments, allUniformValues, allAttributeBindings, std::move(textureBindings), - getID() + this->getID() ); }; @@ -151,191 +165,216 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { auto& geometryTile = static_cast<GeometryTile&>(tile.tile); if (bucket.hasIconData()) { - auto values = iconPropertyValues(evaluated_, layout); - const auto& paintPropertyValues = iconPaintProperties(evaluated_); - - const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point && - layout.get<IconRotationAlignment>() == AlignmentType::Map; - - if (alongLine) { - reprojectLineLabels(bucket.icon.dynamicVertices, - bucket.icon.placedSymbols, - tile.matrix, - values, - tile, - *bucket.iconSizeBinder, - parameters.state); - - parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); - } - - const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; - const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0; - - const gfx::TextureBinding textureBinding{ geometryTile.iconAtlasTexture->getResource(), - bucket.sdfIcons || - parameters.state.isChanging() || - iconScaled || iconTransformed - ? gfx::TextureFilterType::Linear - : gfx::TextureFilterType::Nearest }; + auto renderIcon = [&](const auto& iconSegments) { + auto values = iconPropertyValues(evaluated_, layout); + const auto& paintPropertyValues = iconPaintProperties(evaluated_); + + const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point && + layout.get<IconRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.icon.dynamicVertices, + bucket.icon.placedSymbols, + tile.matrix, + values, + tile, + *bucket.iconSizeBinder, + parameters.state); + + parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); + } - const Size texsize = geometryTile.iconAtlasTexture->size; + const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; + const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0; + + const gfx::TextureBinding textureBinding{ geometryTile.iconAtlasTexture->getResource(), + bucket.sdfIcons || + parameters.state.isChanging() || + iconScaled || iconTransformed + ? gfx::TextureFilterType::Linear + : gfx::TextureFilterType::Nearest }; + + const Size texsize = geometryTile.iconAtlasTexture->size; + + if (bucket.sdfIcons) { + if (values.hasHalo) { + draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, + SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + bucket.icon, + iconSegments, + bucket.iconSizeBinder, + values, + bucketPaintProperties.iconBinders, + paintPropertyValues, + SymbolSDFIconProgram::TextureBindings{ + textureBinding, + }); + } - if (bucket.sdfIcons) { - if (values.hasHalo) { - draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), - bucket.icon, - bucket.iconSizeBinder, - values, - bucketPaintProperties.iconBinders, - paintPropertyValues, - SymbolSDFIconProgram::TextureBindings{ - textureBinding, - }); + if (values.hasFill) { + draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, + SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + bucket.icon, + iconSegments, + bucket.iconSizeBinder, + values, + bucketPaintProperties.iconBinders, + paintPropertyValues, + SymbolSDFIconProgram::TextureBindings{ + textureBinding, + }); + } + } else { + draw(parameters.programs.getSymbolLayerPrograms().symbolIcon, + SymbolIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), + bucket.icon, + iconSegments, + bucket.iconSizeBinder, + values, + bucketPaintProperties.iconBinders, + paintPropertyValues, + SymbolIconProgram::TextureBindings{ + textureBinding, + }); } + }; - if (values.hasFill) { - draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF, - SymbolSDFIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), - bucket.icon, - bucket.iconSizeBinder, - values, - bucketPaintProperties.iconBinders, - paintPropertyValues, - SymbolSDFIconProgram::TextureBindings{ - textureBinding, - }); + if (sortFeaturesByKey) { + for (const auto& segment : bucket.icon.segments) { + renderableSegments.push_back({{std::cref(segment)}, renderIcon, segment.sortKey}); } } else { - draw(parameters.programs.getSymbolLayerPrograms().symbolIcon, - SymbolIconProgram::layoutUniformValues(false, false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange), - bucket.icon, - bucket.iconSizeBinder, - values, - bucketPaintProperties.iconBinders, - paintPropertyValues, - SymbolIconProgram::TextureBindings{ - textureBinding, - }); + renderIcon(bucket.icon.segments); } } if (bucket.hasTextData()) { - const gfx::TextureBinding textureBinding{ geometryTile.glyphAtlasTexture->getResource(), - gfx::TextureFilterType::Linear }; - - auto values = textPropertyValues(evaluated_, layout); - const auto& paintPropertyValues = textPaintProperties(evaluated_); - bool hasVariablePacement = false; - - const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point && - layout.get<TextRotationAlignment>() == AlignmentType::Map; - - if (alongLine) { - reprojectLineLabels(bucket.text.dynamicVertices, - bucket.text.placedSymbols, - tile.matrix, - values, - tile, - *bucket.textSizeBinder, - parameters.state); - - parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); - } else if (!layout.get<TextVariableAnchor>().empty()) { - bucket.text.dynamicVertices.clear(); - - const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(parameters.state.getZoom()); - const float tileScale = std::pow(2, parameters.state.getZoom() - tile.tile.id.overscaledZ); - const bool rotateWithMap = layout.get<TextRotationAlignment>() == AlignmentType::Map; - const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map; - const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, parameters.state.getZoom()); - const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, parameters.state, pixelsToTileUnits); - - for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { - optional<VariableOffset> variableOffset; - if (!symbol.hidden && symbol.crossTileID != 0u) { - auto it = parameters.variableOffsets.get().find(symbol.crossTileID); - if (it != parameters.variableOffsets.get().end()) { - variableOffset = it->second; - hasVariablePacement |= true; - } - } - - 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<float> tileAnchor = symbol.anchorPoint; - const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix); - const float perspectiveRatio = 0.5f + 0.5f * (parameters.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 renderText = [&](const auto& textSegments) { + const gfx::TextureBinding textureBinding{ geometryTile.glyphAtlasTexture->getResource(), + gfx::TextureFilterType::Linear }; + + auto values = textPropertyValues(evaluated_, layout); + const auto& paintPropertyValues = textPaintProperties(evaluated_); + bool hasVariablePacement = false; + + const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point && + layout.get<TextRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.text.dynamicVertices, + bucket.text.placedSymbols, + tile.matrix, + values, + tile, + *bucket.textSizeBinder, + parameters.state); + + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); + } else if (!layout.get<TextVariableAnchor>().empty()) { + bucket.text.dynamicVertices.clear(); + + const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(parameters.state.getZoom()); + const float tileScale = std::pow(2, parameters.state.getZoom() - tile.tile.id.overscaledZ); + const bool rotateWithMap = layout.get<TextRotationAlignment>() == AlignmentType::Map; + const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, parameters.state.getZoom()); + const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, parameters.state, pixelsToTileUnits); + + for (const PlacedSymbol& symbol : bucket.text.placedSymbols) { + optional<VariableOffset> variableOffset; + if (!symbol.hidden && symbol.crossTileID != 0u) { + auto it = parameters.variableOffsets.get().find(symbol.crossTileID); + if (it != parameters.variableOffsets.get().end()) { + variableOffset = it->second; + hasVariablePacement |= true; + } } - 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<float> shiftedAnchor; - if (pitchWithMap) { - shiftedAnchor = project(Point<float>(tileAnchor.x + shift.x, tileAnchor.y + shift.y), - labelPlaneMatrix).first; + 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 { - if (rotateWithMap) { - auto rotated = util::rotate(shift, -parameters.state.getPitch()); - shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x, - projectedAnchor.first.y + rotated.y); + const Point<float> tileAnchor = symbol.anchorPoint; + const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix); + const float perspectiveRatio = 0.5f + 0.5f * (parameters.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<float> shiftedAnchor; + if (pitchWithMap) { + shiftedAnchor = project(Point<float>(tileAnchor.x + shift.x, tileAnchor.y + shift.y), + labelPlaneMatrix).first; } else { - shiftedAnchor = Point<float>(projectedAnchor.first.x + shift.x, - projectedAnchor.first.y + shift.y); + if (rotateWithMap) { + auto rotated = util::rotate(shift, -parameters.state.getPitch()); + shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x, + projectedAnchor.first.y + rotated.y); + } else { + shiftedAnchor = Point<float>(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); + for (std::size_t i = 0; i < symbol.glyphOffsets.size(); i++) { + addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices); + } } } + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); } - parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); - } - const Size texsize = geometryTile.glyphAtlasTexture->size; - - if (values.hasHalo) { - draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), - bucket.text, - bucket.textSizeBinder, - values, - bucketPaintProperties.textBinders, - paintPropertyValues, - SymbolSDFTextProgram::TextureBindings{ - textureBinding, - }); - } + const Size texsize = geometryTile.glyphAtlasTexture->size; - if (values.hasFill) { - draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, - SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), - bucket.text, - bucket.textSizeBinder, - values, - bucketPaintProperties.textBinders, - paintPropertyValues, - SymbolSDFTextProgram::TextureBindings{ - textureBinding, - }); + if (values.hasHalo) { + draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, + SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo), + bucket.text, + textSegments, + bucket.textSizeBinder, + values, + bucketPaintProperties.textBinders, + paintPropertyValues, + SymbolSDFTextProgram::TextureBindings{ + textureBinding, + }); + } + + if (values.hasFill) { + draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph, + SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePacement, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill), + bucket.text, + textSegments, + bucket.textSizeBinder, + values, + bucketPaintProperties.textBinders, + paintPropertyValues, + SymbolSDFTextProgram::TextureBindings{ + textureBinding, + }); + } + }; + + if (sortFeaturesByKey) { + for (const auto& segment : bucket.text.segments) { + renderableSegments.push_back({{std::cref(segment)}, renderText, segment.sortKey}); + } + } else { + renderText(bucket.text.segments); } } @@ -375,6 +414,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { getID() ); } + if (bucket.hasCollisionCircleData()) { static const style::Properties<>::PossiblyEvaluated properties {}; static const CollisionBoxProgram::Binders paintAttributeData(properties, 0); @@ -415,6 +455,16 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { } } + + if (sortFeaturesByKey) { + std::sort(renderableSegments.begin(), renderableSegments.end(), [](const auto& a, const auto& b) { + return a.sortKey < b.sortKey; + }); + + for (auto& renderable : renderableSegments) { + renderable.drawSegments(renderable.segments); + } + } } // static |