Introduce TileWorker
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
#include <mbgl/map/annotation.hpp>
|
||||
#include <mbgl/map/live_tile_data.hpp>
|
||||
#include <mbgl/map/live_tile.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
#include <mbgl/util/work_request.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
@@ -26,28 +26,43 @@ LiveTileData::~LiveTileData() {
|
||||
cancel();
|
||||
}
|
||||
|
||||
void LiveTileData::parse() {
|
||||
if (getState() != State::loaded) {
|
||||
return;
|
||||
bool LiveTileData::reparse(Worker&, std::function<void()> callback) {
|
||||
if (!mayStartParsing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const LiveTile* tile = annotationManager.getTile(id);
|
||||
|
||||
if (tile) {
|
||||
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.
|
||||
TileParser parser(*tile, *this, style);
|
||||
parser.parse();
|
||||
} catch (const std::exception& ex) {
|
||||
Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
|
||||
setState(State::obsolete);
|
||||
workRequest = worker.send([this] {
|
||||
if (getState() != State::loaded) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getState() != State::obsolete) {
|
||||
setState(State::parsed);
|
||||
}
|
||||
const LiveTile* tile = annotationManager.getTile(id);
|
||||
|
||||
if (!tile) {
|
||||
setState(State::parsed);
|
||||
return;
|
||||
}
|
||||
|
||||
TileParseResult result;
|
||||
|
||||
try {
|
||||
result = workerData.parse(*tile);
|
||||
} catch (const std::exception& ex) {
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what();
|
||||
result = message.str();
|
||||
}
|
||||
|
||||
if (getState() == TileData::State::obsolete) {
|
||||
return;
|
||||
} else if (result.is<State>()) {
|
||||
setState(result.get<State>());
|
||||
} else {
|
||||
setError(result.get<std::string>());
|
||||
}
|
||||
|
||||
endParsing();
|
||||
}, callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
bool collisionDebug_);
|
||||
~LiveTileData();
|
||||
|
||||
void parse() override;
|
||||
bool reparse(Worker&, std::function<void ()> callback) override;
|
||||
|
||||
private:
|
||||
AnnotationManager& annotationManager;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace mbgl {
|
||||
TileParser::~TileParser() = default;
|
||||
|
||||
TileParser::TileParser(const GeometryTile& geometryTile_,
|
||||
VectorTileData& tile_,
|
||||
TileWorker& tile_,
|
||||
Style& style_)
|
||||
: geometryTile(geometryTile_),
|
||||
tile(tile_),
|
||||
@@ -98,7 +98,7 @@ void applyLayoutProperty(PropertyKey key, const ClassProperties &classProperties
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc) {
|
||||
// Skip this bucket if we are to not render this
|
||||
if (tile.id.z < std::floor(bucketDesc.min_zoom) && std::floor(bucketDesc.min_zoom) < tile.source.max_zoom) return nullptr;
|
||||
if (tile.id.z < std::floor(bucketDesc.min_zoom) && std::floor(bucketDesc.min_zoom) < tile.maxZoom) return nullptr;
|
||||
if (tile.id.z >= std::ceil(bucketDesc.max_zoom)) return nullptr;
|
||||
if (bucketDesc.visibility == mbgl::VisibilityType::None) return nullptr;
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ class StyleLayoutFill;
|
||||
class StyleLayoutRaster;
|
||||
class StyleLayoutLine;
|
||||
class StyleLayoutSymbol;
|
||||
class VectorTileData;
|
||||
class TileWorker;
|
||||
|
||||
class TileParser : private util::noncopyable {
|
||||
public:
|
||||
TileParser(const GeometryTile&, VectorTileData&, Style&);
|
||||
TileParser(const GeometryTile&, TileWorker&, Style&);
|
||||
~TileParser();
|
||||
|
||||
public:
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
|
||||
|
||||
const GeometryTile& geometryTile;
|
||||
VectorTileData& tile;
|
||||
TileWorker& tile;
|
||||
|
||||
// Cross-thread shared data.
|
||||
Style& style;
|
||||
|
||||
78
src/mbgl/map/tile_worker.cpp
Normal file
78
src/mbgl/map/tile_worker.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/map/tile_worker.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TileWorker::TileWorker(const TileID& id_,
|
||||
Style& style_,
|
||||
const uint16_t maxZoom_,
|
||||
const std::atomic<TileData::State>& state_,
|
||||
std::unique_ptr<CollisionTile> collision_)
|
||||
: id(id_),
|
||||
style(style_),
|
||||
maxZoom(maxZoom_),
|
||||
state(state_),
|
||||
collision(std::move(collision_)) {
|
||||
}
|
||||
|
||||
TileWorker::~TileWorker() {
|
||||
style.glyphAtlas->removeGlyphs(reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
void TileWorker::setBucket(const StyleLayer& 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);
|
||||
}
|
||||
|
||||
Bucket* TileWorker::getBucket(const StyleLayer& layer) const {
|
||||
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 TileWorker::countBuckets() const {
|
||||
std::lock_guard<std::mutex> lock(bucketsMutex);
|
||||
return buckets.size();
|
||||
}
|
||||
|
||||
TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
|
||||
// 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.
|
||||
TileParser parser(geometryTile, *this, style);
|
||||
parser.parse();
|
||||
|
||||
if (parser.isPartialParse()) {
|
||||
return TileData::State::partial;
|
||||
} else {
|
||||
return TileData::State::parsed;
|
||||
}
|
||||
}
|
||||
|
||||
void TileWorker::redoPlacement(float angle, bool collisionDebug) {
|
||||
collision->reset(angle, 0);
|
||||
collision->setDebug(collisionDebug);
|
||||
for (const auto& layer_desc : style.layers) {
|
||||
auto bucket = getBucket(*layer_desc);
|
||||
if (bucket) {
|
||||
bucket->placeFeatures();
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/mbgl/map/tile_worker.hpp
Normal file
75
src/mbgl/map/tile_worker.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef MBGL_MAP_TILE_WORKER
|
||||
#define MBGL_MAP_TILE_WORKER
|
||||
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/fill_buffer.hpp>
|
||||
#include <mbgl/geometry/line_buffer.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class CollisionTile;
|
||||
class GeometryTile;
|
||||
class Style;
|
||||
class Bucket;
|
||||
|
||||
using TileParseResult = mapbox::util::variant<
|
||||
TileData::State, // success
|
||||
std::string>; // error
|
||||
|
||||
class TileWorker : public util::noncopyable {
|
||||
public:
|
||||
TileWorker(const TileID&,
|
||||
Style&,
|
||||
const uint16_t maxZoom,
|
||||
const std::atomic<TileData::State>&,
|
||||
std::unique_ptr<CollisionTile>);
|
||||
~TileWorker();
|
||||
|
||||
void setBucket(const StyleLayer&, std::unique_ptr<Bucket>);
|
||||
Bucket* getBucket(const StyleLayer&) const;
|
||||
size_t countBuckets() const;
|
||||
|
||||
inline TileData::State getState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
inline CollisionTile* getCollision() const {
|
||||
return collision.get();
|
||||
}
|
||||
|
||||
TileParseResult parse(const GeometryTile&);
|
||||
void redoPlacement(float angle, bool collisionDebug);
|
||||
|
||||
const TileID id;
|
||||
Style& style;
|
||||
const uint16_t maxZoom;
|
||||
const std::atomic<TileData::State>& state;
|
||||
|
||||
FillVertexBuffer fillVertexBuffer;
|
||||
LineVertexBuffer lineVertexBuffer;
|
||||
|
||||
TriangleElementsBuffer triangleElementsBuffer;
|
||||
LineElementsBuffer lineElementsBuffer;
|
||||
|
||||
std::unique_ptr<CollisionTile> collision;
|
||||
|
||||
// Contains all the Bucket objects for the tile. Buckets are render
|
||||
// objects and they get added to this std::map<> by the workers doing
|
||||
// the actual tile parsing as they get processed. Tiles partially
|
||||
// parsed can get new buckets at any moment but are also fit for
|
||||
// rendering. That said, access to this list needs locking unless
|
||||
// the tile is completely parsed.
|
||||
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
|
||||
mutable std::mutex bucketsMutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,8 @@
|
||||
#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/map/vector_tile.hpp>
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
@@ -22,8 +20,14 @@ VectorTileData::VectorTileData(const TileID& id_,
|
||||
float angle,
|
||||
bool collisionDebug)
|
||||
: TileData(id_, source_),
|
||||
style(style_),
|
||||
collision(std::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
|
||||
worker(style_.workers),
|
||||
workerData(id_,
|
||||
style_,
|
||||
source.max_zoom,
|
||||
state,
|
||||
std::make_unique<CollisionTile>(id_.z, 4096,
|
||||
source_.tile_size * id.overscaling,
|
||||
angle, collisionDebug)),
|
||||
lastAngle(angle),
|
||||
currentAngle(angle) {
|
||||
}
|
||||
@@ -32,17 +36,16 @@ VectorTileData::~VectorTileData() {
|
||||
// Cancel in most derived class destructor so that worker tasks are joined before
|
||||
// any member data goes away.
|
||||
cancel();
|
||||
style.glyphAtlas->removeGlyphs(reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
void VectorTileData::request(Worker& worker,
|
||||
void VectorTileData::request(Worker&,
|
||||
float pixelRatio,
|
||||
const std::function<void()>& callback) {
|
||||
std::string url = source.tileURL(id, pixelRatio);
|
||||
state = State::loading;
|
||||
|
||||
FileSource* fs = util::ThreadContext::getFileSource();
|
||||
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::current.get()->get(), [url, callback, &worker, this](const Response &res) {
|
||||
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::current.get()->get(), [url, callback, this](const Response &res) {
|
||||
req = nullptr;
|
||||
|
||||
if (res.status != Response::Successful) {
|
||||
@@ -56,89 +59,62 @@ void VectorTileData::request(Worker& worker,
|
||||
state = State::loaded;
|
||||
data = res.data;
|
||||
|
||||
// Schedule tile parsing in another thread
|
||||
reparse(worker, callback);
|
||||
});
|
||||
}
|
||||
|
||||
bool VectorTileData::reparse(Worker& worker, std::function<void()> callback) {
|
||||
bool VectorTileData::reparse(Worker&, std::function<void()> callback) {
|
||||
if (!mayStartParsing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
workRequest = worker.send([this] { parse(); endParsing(); }, callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
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, style);
|
||||
parser.parse();
|
||||
|
||||
if (getState() == State::obsolete) {
|
||||
workRequest = worker.send([this] {
|
||||
if (getState() != TileData::State::loaded && getState() != TileData::State::partial) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parser.isPartialParse()) {
|
||||
setState(State::partial);
|
||||
} else {
|
||||
setState(State::parsed);
|
||||
TileParseResult result;
|
||||
|
||||
try {
|
||||
VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size()));
|
||||
result = workerData.parse(vectorTile);
|
||||
} catch (const std::exception& ex) {
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what();
|
||||
result = message.str();
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what();
|
||||
setError(message.str());
|
||||
}
|
||||
|
||||
if (getState() == TileData::State::obsolete) {
|
||||
return;
|
||||
} else if (result.is<State>()) {
|
||||
setState(result.get<State>());
|
||||
} else {
|
||||
setError(result.get<std::string>());
|
||||
}
|
||||
|
||||
endParsing();
|
||||
}, callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Bucket* VectorTileData::getBucket(StyleLayer const& layer) {
|
||||
Bucket* VectorTileData::getBucket(const StyleLayer& 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();
|
||||
return workerData.getBucket(layer);
|
||||
}
|
||||
|
||||
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);
|
||||
return workerData.countBuckets();
|
||||
}
|
||||
|
||||
void VectorTileData::setState(const State& state_) {
|
||||
TileData::setState(state_);
|
||||
|
||||
if (isImmutable()) {
|
||||
collision->reset(0, 0);
|
||||
workerData.collision->reset(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,24 +134,12 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) {
|
||||
currentCollisionDebug = collisionDebug;
|
||||
|
||||
auto callback = std::bind(&VectorTileData::endRedoPlacement, this);
|
||||
workRequest = style.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 : style.layers) {
|
||||
auto bucket = getBucket(*layer_desc);
|
||||
if (bucket) {
|
||||
bucket->placeFeatures();
|
||||
}
|
||||
workRequest = worker.send([this, angle, collisionDebug] { workerData.redoPlacement(angle, collisionDebug); }, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void VectorTileData::endRedoPlacement() {
|
||||
for (const auto& layer_desc : style.layers) {
|
||||
for (const auto& layer_desc : workerData.style.layers) {
|
||||
auto bucket = getBucket(*layer_desc);
|
||||
if (bucket) {
|
||||
bucket->swapRenderData();
|
||||
|
||||
@@ -2,31 +2,16 @@
|
||||
#define MBGL_MAP_VECTOR_TILE_DATA
|
||||
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
#include <mbgl/geometry/elements_buffer.hpp>
|
||||
#include <mbgl/geometry/fill_buffer.hpp>
|
||||
#include <mbgl/geometry/icon_buffer.hpp>
|
||||
#include <mbgl/geometry/line_buffer.hpp>
|
||||
#include <mbgl/geometry/text_buffer.hpp>
|
||||
#include <mbgl/geometry/collision_box_buffer.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <mbgl/map/tile_worker.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Bucket;
|
||||
class CollisionTile;
|
||||
class Painter;
|
||||
class SourceInfo;
|
||||
class StyleLayer;
|
||||
class TileParser;
|
||||
class Style;
|
||||
|
||||
class VectorTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
VectorTileData(const TileID&,
|
||||
Style&,
|
||||
@@ -35,18 +20,11 @@ public:
|
||||
bool collisionDebug_);
|
||||
~VectorTileData();
|
||||
|
||||
virtual void parse();
|
||||
void redoPlacement(float angle, bool collisionDebug) override;
|
||||
virtual Bucket* getBucket(StyleLayer const &layer_desc) override;
|
||||
|
||||
size_t countBuckets() const;
|
||||
void setBucket(StyleLayer const &layer_desc, std::unique_ptr<Bucket> bucket);
|
||||
|
||||
void setState(const State& state) override;
|
||||
|
||||
inline CollisionTile* getCollision() const {
|
||||
return collision.get();
|
||||
}
|
||||
Bucket* getBucket(const StyleLayer&) override;
|
||||
size_t countBuckets() const;
|
||||
|
||||
void request(Worker&,
|
||||
float pixelRatio,
|
||||
@@ -57,34 +35,16 @@ public:
|
||||
protected:
|
||||
void redoPlacement();
|
||||
|
||||
// Holds the actual geometries in this tile.
|
||||
FillVertexBuffer fillVertexBuffer;
|
||||
LineVertexBuffer lineVertexBuffer;
|
||||
|
||||
TriangleElementsBuffer triangleElementsBuffer;
|
||||
LineElementsBuffer lineElementsBuffer;
|
||||
|
||||
Style& style;
|
||||
Worker& worker;
|
||||
TileWorker workerData;
|
||||
|
||||
private:
|
||||
// Contains all the Bucket objects for the tile. Buckets are render
|
||||
// objects and they get added to this std::map<> by the workers doing
|
||||
// the actual tile parsing as they get processed. Tiles partially
|
||||
// parsed can get new buckets at any moment but are also fit for
|
||||
// rendering. That said, access to this list needs locking unless
|
||||
// the tile is completely parsed.
|
||||
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
|
||||
mutable std::mutex bucketsMutex;
|
||||
|
||||
std::unique_ptr<CollisionTile> collision;
|
||||
|
||||
float lastAngle = 0;
|
||||
float currentAngle;
|
||||
bool lastCollisionDebug = 0;
|
||||
bool currentCollisionDebug = 0;
|
||||
bool redoingPlacement = false;
|
||||
void endRedoPlacement();
|
||||
void workerRedoPlacement(float angle, bool collisionDebug);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user