#include #include #include #include #include namespace mbgl { using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_, const std::map>& paintProperties_, const style::PropertyValue& textSize, const style::PropertyValue& iconSize, float zoom, bool sdfIcons_, bool iconsNeedLinear_, bool sortFeaturesByY_, const std::string bucketName_, const std::vector&& symbolInstances_, float tilePixelRatio_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), sortFeaturesByY(sortFeaturesByY_), bucketLeaderID(std::move(bucketName_)), symbolInstances(std::move(symbolInstances_)), textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())), tilePixelRatio(tilePixelRatio_) { for (const auto& pair : paintProperties_) { const auto& evaluated = getEvaluated(pair.second); paintProperties.emplace( std::piecewise_construct, std::forward_as_tuple(pair.first), std::forward_as_tuple(PaintProperties { { RenderSymbolLayer::iconPaintProperties(evaluated), zoom }, { RenderSymbolLayer::textPaintProperties(evaluated), zoom } })); } } SymbolBucket::~SymbolBucket() = default; void SymbolBucket::upload(gfx::Context& context) { if (hasTextData()) { if (!staticUploaded) { text.indexBuffer = context.createIndexBuffer(std::move(text.triangles), sortFeaturesByY ? gfx::BufferUsageType::StreamDraw : gfx::BufferUsageType::StaticDraw); text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); for (auto& pair : paintProperties) { pair.second.textBinders.upload(context); } } else if (!sortUploaded) { context.updateIndexBuffer(*text.indexBuffer, std::move(text.triangles)); } if (!dynamicUploaded) { text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gfx::BufferUsageType::StreamDraw); } if (!placementChangesUploaded) { if (!text.opacityVertexBuffer) { text.opacityVertexBuffer = context.createVertexBuffer(std::move(text.opacityVertices), gfx::BufferUsageType::StreamDraw); } else { context.updateVertexBuffer(*text.opacityVertexBuffer, std::move(text.opacityVertices)); } } } if (hasIconData()) { if (!staticUploaded) { icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gfx::BufferUsageType::StreamDraw : gfx::BufferUsageType::StaticDraw); icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); for (auto& pair : paintProperties) { pair.second.iconBinders.upload(context); } } else if (!sortUploaded) { context.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles)); } if (!dynamicUploaded) { icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw); } if (!placementChangesUploaded) { if (!icon.opacityVertexBuffer) { icon.opacityVertexBuffer = context.createVertexBuffer(std::move(icon.opacityVertices), gfx::BufferUsageType::StreamDraw); } else { context.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices)); } } } if (hasCollisionBoxData()) { if (!staticUploaded) { collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines)); collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices)); } if (!placementChangesUploaded) { if (!collisionBox.dynamicVertexBuffer) { collisionBox.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionBox.dynamicVertices), gfx::BufferUsageType::StreamDraw); } else { context.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices)); } } } if (hasCollisionCircleData()) { if (!staticUploaded) { collisionCircle.indexBuffer = context.createIndexBuffer(std::move(collisionCircle.triangles)); collisionCircle.vertexBuffer = context.createVertexBuffer(std::move(collisionCircle.vertices)); } if (!placementChangesUploaded) { if (!collisionCircle.dynamicVertexBuffer) { collisionCircle.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gfx::BufferUsageType::StreamDraw); } else { context.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices)); } } } uploaded = true; staticUploaded = true; placementChangesUploaded = true; dynamicUploaded = true; sortUploaded = true; } bool SymbolBucket::hasData() const { return hasTextData() || hasIconData() || hasCollisionBoxData(); } bool SymbolBucket::supportsLayer(const style::Layer::Impl& impl) const { return style::SymbolLayer::Impl::staticTypeInfo() == impl.getTypeInfo(); } bool SymbolBucket::hasTextData() const { return !text.segments.empty(); } bool SymbolBucket::hasIconData() const { return !icon.segments.empty(); } bool SymbolBucket::hasCollisionBoxData() const { return !collisionBox.segments.empty(); } bool SymbolBucket::hasCollisionCircleData() const { return !collisionCircle.segments.empty(); } void SymbolBucket::updateOpacity() { placementChangesUploaded = false; uploaded = false; } void addPlacedSymbol(gfx::IndexVector& triangles, const PlacedSymbol& placedSymbol) { auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4; for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) { triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2); triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3); } } void SymbolBucket::sortFeatures(const float angle) { if (!sortFeaturesByY) { return; } if (sortedAngle && *sortedAngle == angle) { return; } sortedAngle = angle; // The current approach to sorting doesn't sort across segments so don't try. // Sorting within segments separately seemed not to be worth the complexity. if (text.segments.size() > 1 || icon.segments.size() > 1) { return; } sortUploaded = false; uploaded = false; text.triangles.clear(); icon.triangles.clear(); featureSortOrder = std::make_unique>(); featureSortOrder->reserve(symbolInstances.size()); // If the symbols are allowed to overlap sort them by their vertical screen position. // The index array buffer is rewritten to reference the (unchanged) vertices in the // sorted order. for (const SymbolInstance& symbolInstance : getSortedSymbols(angle)) { featureSortOrder->push_back(symbolInstance.dataFeatureIndex); if (symbolInstance.placedRightTextIndex) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedRightTextIndex]); } if (symbolInstance.placedCenterTextIndex && !symbolInstance.singleLine) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedCenterTextIndex]); } if (symbolInstance.placedLeftTextIndex && !symbolInstance.singleLine) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedLeftTextIndex]); } if (symbolInstance.placedVerticalTextIndex) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]); } if (symbolInstance.placedIconIndex) { addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]); } } } std::vector> SymbolBucket::getSortedSymbols(const float angle) { std::vector> result(symbolInstances.begin(), symbolInstances.end()); const float sin = std::sin(angle); const float cos = std::cos(angle); std::sort(result.begin(), result.end(), [sin, cos](const SymbolInstance& a, const SymbolInstance& b) { const auto aRotated = ::lround(sin * a.anchor.point.x + cos * a.anchor.point.y); const auto bRotated = ::lround(sin * b.anchor.point.x + cos * b.anchor.point.y); if (aRotated != bRotated) { return aRotated < bRotated; } return a.dataFeatureIndex > b.dataFeatureIndex; // aRotated == bRotated }); return result; } bool SymbolBucket::hasFormatSectionOverrides() const { if (!hasFormatSectionOverrides_) { hasFormatSectionOverrides_= SymbolLayerPaintPropertyOverrides::hasOverrides(layout.get()); } return *hasFormatSectionOverrides_; } } // namespace mbgl