#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace mbgl; TileWorker::TileWorker(TileID id_, std::string sourceID_, SpriteStore& spriteStore_, GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_, const std::atomic& state_, const MapMode mode_) : id(id_), sourceID(std::move(sourceID_)), spriteStore(spriteStore_), glyphAtlas(glyphAtlas_), glyphStore(glyphStore_), state(state_), mode(mode_) { } TileWorker::~TileWorker() { glyphAtlas.removeGlyphs(reinterpret_cast(this)); } TileParseResult TileWorker::parseAllLayers(std::vector> layers_, std::unique_ptr geometryTile, PlacementConfig config) { // We're doing a fresh parse of the tile, because the underlying data has changed. pending.clear(); placementPending.clear(); partialParse = false; // Store the layers for use in redoPlacement. layers = std::move(layers_); // We're storing a set of bucket names we've parsed to avoid parsing a bucket twice that is // referenced from more than one layer std::set parsed; for (auto i = layers.rbegin(); i != layers.rend(); i++) { const StyleLayer* layer = i->get(); if (parsed.find(layer->bucketName()) == parsed.end()) { parsed.emplace(layer->bucketName()); parseLayer(layer, *geometryTile); } } result.state = pending.empty() ? TileData::State::parsed : TileData::State::partial; if (result.state == TileData::State::parsed) { placeLayers(config); } return std::move(result); } TileParseResult TileWorker::parsePendingLayers(const PlacementConfig config) { // Try parsing the remaining layers that we couldn't parse in the first step due to missing // dependencies. for (auto it = pending.begin(); it != pending.end();) { auto& layer = *it->first; auto bucket = dynamic_cast(it->second.get()); assert(bucket); // Only symbol layers can be pending, so the dynamic cast should never fail. if (!bucket->needsDependencies(glyphStore, spriteStore)) { bucket->addFeatures(reinterpret_cast(this), *layer.spriteAtlas, glyphAtlas, glyphStore); placementPending.emplace(layer.bucketName(), std::move(it->second)); pending.erase(it++); continue; } // Advance the iterator here; we're skipping this when erasing an element from this list. ++it; } result.state = pending.empty() ? TileData::State::parsed : TileData::State::partial; if (result.state == TileData::State::parsed) { placeLayers(config); } return std::move(result); } void TileWorker::placeLayers(const PlacementConfig config) { redoPlacement(&placementPending, config); for (auto &p : placementPending) { p.second->swapRenderData(); insertBucket(p.first, std::move(p.second)); } placementPending.clear(); } void TileWorker::redoPlacement( const std::unordered_map>* buckets, PlacementConfig config) { CollisionTile collisionTile(config); for (auto i = layers.rbegin(); i != layers.rend(); i++) { const auto it = buckets->find((*i)->id); if (it != buckets->end()) { it->second->placeFeatures(collisionTile); } } } void TileWorker::parseLayer(const StyleLayer* layer, const GeometryTile& geometryTile) { // Cancel early when parsing. if (state == TileData::State::obsolete) return; // Background and custom layers are special cases. if (layer->is() || layer->is()) return; // Skip this bucket if we are to not render this if ((layer->source != sourceID) || (id.z < std::floor(layer->minZoom)) || (id.z >= std::ceil(layer->maxZoom)) || (layer->visibility == VisibilityType::None)) { return; } auto geometryLayer = geometryTile.getLayer(layer->sourceLayer); if (!geometryLayer) { // The layer specified in the bucket does not exist. Do nothing. if (debug::tileParseWarnings) { Log::Warning(Event::ParseTile, "layer '%s' does not exist in tile %d/%d/%d", layer->sourceLayer.c_str(), id.z, id.x, id.y); } return; } StyleBucketParameters parameters(id, *geometryLayer, state, reinterpret_cast(this), partialParse, spriteStore, glyphAtlas, glyphStore, mode); std::unique_ptr bucket = layer->createBucket(parameters); if (layer->is()) { if (partialParse) { // We cannot parse this bucket yet. Instead, we're saving it for later. pending.emplace_back(layer->as(), std::move(bucket)); } else { placementPending.emplace(layer->bucketName(), std::move(bucket)); } } else { insertBucket(layer->bucketName(), std::move(bucket)); } } void TileWorker::insertBucket(const std::string& name, std::unique_ptr bucket) { if (bucket->hasData()) { result.buckets.emplace(name, std::move(bucket)); } }