Layers are added and removed dynamically on the Map thread when we use shape annotations and we are iterating style.layers on the Worker thread without any lock. Now when we create a [Live|Vector]TileData at the Map thread we give it a layers list (instead of the Style) and [Live|Vector]TileData will ultimately make a copy of this list (on the Map thread, so no concurrency with adding shape annotations). The copy should be relatively cheap because we are using a shared pointer for storing the layers on the list.
157 lines
4.7 KiB
C++
157 lines
4.7 KiB
C++
#include <mbgl/map/vector_tile_data.hpp>
|
|
#include <mbgl/map/tile_parser.hpp>
|
|
#include <mbgl/style/style_layer.hpp>
|
|
#include <mbgl/style/style_bucket.hpp>
|
|
#include <mbgl/map/source.hpp>
|
|
#include <mbgl/geometry/glyph_atlas.hpp>
|
|
#include <mbgl/platform/log.hpp>
|
|
#include <mbgl/text/collision_tile.hpp>
|
|
#include <mbgl/util/pbf.hpp>
|
|
#include <mbgl/util/worker.hpp>
|
|
#include <mbgl/util/work_request.hpp>
|
|
|
|
using namespace mbgl;
|
|
|
|
VectorTileData::VectorTileData(const TileID& id_,
|
|
const std::vector<util::ptr<StyleLayer>>& layers_,
|
|
Worker& workers_,
|
|
GlyphAtlas& glyphAtlas_,
|
|
GlyphStore& glyphStore_,
|
|
SpriteAtlas& spriteAtlas_,
|
|
util::ptr<Sprite> sprite_,
|
|
const SourceInfo& source_,
|
|
float angle,
|
|
bool collisionDebug)
|
|
: TileData(id_, source_),
|
|
layers(layers_),
|
|
workers(workers_),
|
|
glyphAtlas(glyphAtlas_),
|
|
glyphStore(glyphStore_),
|
|
spriteAtlas(spriteAtlas_),
|
|
sprite(sprite_),
|
|
collision(std::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
|
|
lastAngle(angle),
|
|
currentAngle(angle) {
|
|
}
|
|
|
|
VectorTileData::~VectorTileData() {
|
|
// Cancel in most derived class destructor so that worker tasks are joined before
|
|
// any member data goes away.
|
|
cancel();
|
|
glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
|
|
}
|
|
|
|
void VectorTileData::parse() {
|
|
if (getState() != State::loaded && getState() != State::partial) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
|
// the TileParser object writes results into this objects. All other state
|
|
// is going to be discarded afterwards.
|
|
VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size()));
|
|
const VectorTile* vt = &vectorTile;
|
|
TileParser parser(*vt, *this, layers, glyphAtlas, glyphStore, spriteAtlas, sprite);
|
|
parser.parse();
|
|
|
|
if (getState() == State::obsolete) {
|
|
return;
|
|
}
|
|
|
|
if (parser.isPartialParse()) {
|
|
setState(State::partial);
|
|
} else {
|
|
setState(State::parsed);
|
|
}
|
|
} catch (const std::exception& ex) {
|
|
std::stringstream message;
|
|
message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what();
|
|
setError(message.str());
|
|
}
|
|
}
|
|
|
|
Bucket* VectorTileData::getBucket(StyleLayer const& layer) {
|
|
if (!isReady() || !layer.bucket) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(bucketsMutex);
|
|
|
|
const auto it = buckets.find(layer.bucket->name);
|
|
if (it == buckets.end()) {
|
|
return nullptr;
|
|
}
|
|
|
|
assert(it->second);
|
|
return it->second.get();
|
|
}
|
|
|
|
size_t VectorTileData::countBuckets() const {
|
|
std::lock_guard<std::mutex> lock(bucketsMutex);
|
|
|
|
return buckets.size();
|
|
}
|
|
|
|
void VectorTileData::setBucket(StyleLayer const& layer, std::unique_ptr<Bucket> bucket) {
|
|
assert(layer.bucket);
|
|
|
|
std::lock_guard<std::mutex> lock(bucketsMutex);
|
|
|
|
if (buckets.find(layer.bucket->name) != buckets.end()) {
|
|
return;
|
|
}
|
|
|
|
buckets[layer.bucket->name] = std::move(bucket);
|
|
}
|
|
|
|
void VectorTileData::setState(const State& state_) {
|
|
TileData::setState(state_);
|
|
|
|
if (isImmutable()) {
|
|
collision->reset(0, 0);
|
|
}
|
|
}
|
|
|
|
void VectorTileData::redoPlacement() {
|
|
redoPlacement(lastAngle, lastCollisionDebug);
|
|
}
|
|
|
|
void VectorTileData::redoPlacement(float angle, bool collisionDebug) {
|
|
if (angle != currentAngle || collisionDebug != currentCollisionDebug) {
|
|
lastAngle = angle;
|
|
lastCollisionDebug = collisionDebug;
|
|
|
|
if (getState() != State::parsed || redoingPlacement) return;
|
|
|
|
redoingPlacement = true;
|
|
currentAngle = angle;
|
|
currentCollisionDebug = collisionDebug;
|
|
|
|
auto callback = std::bind(&VectorTileData::endRedoPlacement, this);
|
|
workRequest = workers.send([this, angle, collisionDebug] { workerRedoPlacement(angle, collisionDebug); }, callback);
|
|
}
|
|
}
|
|
|
|
void VectorTileData::workerRedoPlacement(float angle, bool collisionDebug) {
|
|
collision->reset(angle, 0);
|
|
collision->setDebug(collisionDebug);
|
|
for (const auto& layer_desc : layers) {
|
|
auto bucket = getBucket(*layer_desc);
|
|
if (bucket) {
|
|
bucket->placeFeatures();
|
|
}
|
|
}
|
|
}
|
|
|
|
void VectorTileData::endRedoPlacement() {
|
|
for (const auto& layer_desc : layers) {
|
|
auto bucket = getBucket(*layer_desc);
|
|
if (bucket) {
|
|
bucket->swapRenderData();
|
|
}
|
|
}
|
|
redoingPlacement = false;
|
|
redoPlacement();
|
|
}
|