[core] reparse tiles when new data arrives

We're now reparsing tiles when they expire. We're also swapping out buckets atomically to avoid flickering data; i.e. we're displaying the old data as long as we don't have a new parsed bucket for that layer yet. The parsed buckets now live in the *TileData objects rather than in the TileWorker; only partially parsed == pending buckets will remain in the TileWorker. Once they're parsed, they're moved to the *TileData object.
This commit is contained in:
Konstantin Käfer
2015-10-20 12:19:11 +02:00
parent 5173bf1bb8
commit c0c554e36f
27 changed files with 467 additions and 289 deletions

View File

@@ -2,6 +2,7 @@
#define MBGL_STORAGE_RESPONSE
#include <string>
#include <memory>
namespace mbgl {

View File

@@ -6,6 +6,7 @@
#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <sstream>
@@ -32,21 +33,27 @@ LiveTileData::LiveTileData(const TileID& id_,
return;
}
reparse(callback);
parsePending(callback);
}
bool LiveTileData::reparse(std::function<void()> callback) {
if (parsing || (state != State::loaded && state != State::partial)) {
bool LiveTileData::parsePending(std::function<void()> callback) {
if (workRequest || (state != State::loaded && state != State::partial)) {
return false;
}
parsing = true;
workRequest = worker.parseLiveTile(tileWorker, *tile, [this, callback] (TileParseResult result) {
parsing = false;
workRequest.reset();
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
if (result.is<State>()) {
state = result.get<State>();
} else {
error = result.get<std::string>();
state = State::obsolete;
@@ -67,7 +74,13 @@ Bucket* LiveTileData::getBucket(const StyleLayer& layer) {
return nullptr;
}
return tileWorker.getBucket(layer);
const auto it = buckets.find(layer.bucket->name);
if (it == buckets.end()) {
return nullptr;
}
assert(it->second);
return it->second.get();
}
void LiveTileData::cancel() {

View File

@@ -20,7 +20,7 @@ public:
std::function<void ()> callback);
~LiveTileData();
bool reparse(std::function<void ()> callback) override;
bool parsePending(std::function<void ()> callback) override;
void cancel() override;
Bucket* getBucket(const StyleLayer&) override;
@@ -29,8 +29,11 @@ private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<WorkRequest> workRequest;
bool parsing = false;
std::unique_ptr<AnnotationTile> tile;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added by tile parsing operations.
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
};
}

View File

@@ -11,13 +11,13 @@
using namespace mbgl;
RasterTileData::RasterTileData(const TileID& id_,
TexturePool &texturePool,
TexturePool &texturePool_,
const SourceInfo &source_,
Worker& worker_)
: TileData(id_),
texturePool(texturePool_),
source(source_),
worker(worker_),
bucket(texturePool, layout) {
worker(worker_) {
}
RasterTileData::~RasterTileData() {
@@ -52,15 +52,24 @@ void RasterTileData::request(float pixelRatio,
return;
}
state = State::loaded;
if (state == State::loading) {
// Only overwrite the state when we didn't have a previous tile.
state = State::loaded;
}
workRequest = worker.parseRasterTile(bucket, res.data, [this, callback] (TileParseResult result) {
workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(texturePool, layout), res.data, [this, callback] (TileParseResult result) {
workRequest.reset();
if (state != State::loaded) {
return;
}
if (result.is<State>()) {
state = result.get<State>();
if (result.is<TileParseResultBuckets>()) {
auto& buckets = result.get<TileParseResultBuckets>();
state = buckets.state;
// TODO: Make this less awkward; we're only getting one bucket back.
if (!buckets.buckets.empty()) {
bucket = std::move(buckets.buckets.front().second);
}
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
@@ -74,7 +83,7 @@ void RasterTileData::request(float pixelRatio,
}
Bucket* RasterTileData::getBucket(StyleLayer const&) {
return &bucket;
return bucket.get();
}
void RasterTileData::cancel() {

View File

@@ -27,12 +27,13 @@ public:
Bucket* getBucket(StyleLayer const &layer_desc) override;
private:
TexturePool& texturePool;
const SourceInfo& source;
Worker& worker;
RequestHolder req;
RasterLayoutProperties layout;
RasterBucket bucket;
std::unique_ptr<Bucket> bucket;
std::unique_ptr<WorkRequest> workRequest;
};

View File

@@ -242,7 +242,7 @@ bool Source::handlePartialTile(const TileID& id, Worker&) {
return true;
}
return data->reparse([this]() {
return data->parsePending([this]() {
emitTileLoaded(false);
});
}
@@ -364,7 +364,8 @@ bool Source::findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::
const TileData::State state = hasTile(child_id);
if (TileData::isReadyState(state)) {
retain.emplace_front(child_id);
} else {
}
if (state != TileData::State::parsed) {
complete = false;
if (z < maxCoveringZoom) {
// Go further down the hierarchy to find more unloaded children.
@@ -384,16 +385,17 @@ bool Source::findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::
*
* @return boolean Whether a parent was found.
*/
bool Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain) {
void Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain) {
for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
const TileID parent_id = id.parent(z, info.max_zoom);
const TileData::State state = hasTile(parent_id);
if (TileData::isReadyState(state)) {
retain.emplace_front(parent_id);
return true;
if (state == TileData::State::parsed) {
return;
}
}
}
return false;
}
bool Source::update(MapData& data,

View File

@@ -105,7 +105,7 @@ private:
bool handlePartialTile(const TileID &id, Worker &worker);
bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain);
bool findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain);
void findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain);
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<TileID> coveringTiles(const TransformState&) const;

View File

@@ -1,11 +1,26 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
using namespace mbgl;
namespace mbgl {
TileData::TileData(const TileID& id_)
: id(id_),
debugBucket(debugFontBuffer),
state(State::initial) {
// Initialize tile debug coordinates
debugFontBuffer.addText(std::string(id).c_str(), 50, 200, 5);
}
TileData::~TileData() = default;
const char* TileData::StateToString(const State state) {
switch (state) {
case TileData::State::initial: return "initial";
case TileData::State::invalid : return "invalid";
case TileData::State::loading : return "loading";
case TileData::State::loaded : return "loaded";
case TileData::State::obsolete : return "obsolete";
case TileData::State::parsed : return "parsed";
case TileData::State::partial : return "partial";
default: return "<unknown>";
}
}
} // namespace mbgl

View File

@@ -3,17 +3,18 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/renderer/bucket.hpp>
#include <atomic>
#include <string>
#include <memory>
#include <functional>
namespace mbgl {
class StyleLayer;
class Worker;
class DebugBucket;
class TileData : private util::noncopyable {
public:
@@ -57,6 +58,8 @@ public:
obsolete
};
static const char* StateToString(State);
// Tile data considered "Ready" can be used for rendering. Data in
// partial state is still waiting for network resources but can also
// be rendered, although layers will be missing.
@@ -65,14 +68,14 @@ public:
}
TileData(const TileID&);
virtual ~TileData() = default;
virtual ~TileData();
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
virtual Bucket* getBucket(const StyleLayer&) = 0;
virtual bool reparse(std::function<void ()>) { return true; }
virtual bool parsePending(std::function<void ()>) { return true; }
virtual void redoPlacement(float, float, bool) {}
bool isReady() const {
@@ -90,14 +93,13 @@ public:
const TileID id;
// Contains the tile ID string for painting debug information.
DebugBucket debugBucket;
DebugFontBuffer debugFontBuffer;
std::unique_ptr<DebugBucket> debugBucket;
protected:
std::atomic<State> state;
std::string error;
};
}
} // namespace mbgl
#endif

View File

@@ -3,6 +3,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
@@ -10,6 +11,7 @@
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/exception.hpp>
using namespace mbgl;
@@ -33,34 +35,66 @@ TileWorker::~TileWorker() {
style.glyphAtlas->removeGlyphs(reinterpret_cast<uintptr_t>(this));
}
Bucket* TileWorker::getBucket(const StyleLayer& layer) const {
std::lock_guard<std::mutex> lock(bucketsMutex);
TileParseResult TileWorker::parseAllLayers(const GeometryTile& geometryTile) {
// We're doing a fresh parse of the tile, because the underlying data has changed.
pending.clear();
const auto it = buckets.find(layer.bucket->name);
if (it == buckets.end()) {
return nullptr;
}
assert(it->second);
return it->second.get();
}
TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
partialParse = false;
// We're storing a list of buckets we've parsed to avoid parsing a bucket twice that is
// referenced from more than one layer
std::set<StyleBucket*> parsed;
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
parseLayer(**i, geometryTile);
const StyleLayer& layer = **i;
if (layer.bucket && parsed.find(layer.bucket.get()) == parsed.end()) {
parsed.emplace(layer.bucket.get());
parseLayer(layer, geometryTile);
}
}
return partialParse ? TileData::State::partial : TileData::State::parsed;
result.state = pending.empty() ? TileData::State::parsed : TileData::State::partial;
return std::move(result);
}
void TileWorker::redoPlacement(float angle, float pitch, bool collisionDebug) {
TileParseResult TileWorker::parsePendingLayers() {
// 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& styleBucket = it->first;
auto& bucket = it->second;
assert(bucket);
if (styleBucket.type == StyleLayerType::Symbol) {
auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.get());
if (!symbolBucket->needsDependencies(*style.glyphStore, *style.sprite)) {
symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
*style.glyphAtlas, *style.glyphStore, *collisionTile);
insertBucket(styleBucket.name, std::move(bucket));
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;
return std::move(result);
}
void TileWorker::redoPlacement(
const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
float angle,
float pitch,
bool collisionDebug) {
// Reset the collision tile so we have a clean slate; we're placing all features anyway.
collisionTile = std::make_unique<CollisionTile>(angle, pitch, collisionDebug);
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
auto bucket = getBucket(**i);
if (bucket) {
bucket->placeFeatures(*collisionTile);
const auto it = buckets->find((*i)->id);
if (it != buckets->end()) {
it->second->placeFeatures(*collisionTile);
}
}
}
@@ -88,21 +122,15 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
return;
}
// This is a singular layer. Check if this bucket already exists.
if (getBucket(layer))
return;
const StyleBucket& styleBucket = *layer.bucket;
// Skip this bucket if we are to not render this
if (styleBucket.source != sourceID)
return;
if (id.z < std::floor(styleBucket.min_zoom))
return;
if (id.z >= std::ceil(styleBucket.max_zoom))
return;
if (styleBucket.visibility == mbgl::VisibilityType::None)
if ((styleBucket.source != sourceID) ||
(id.z < std::floor(styleBucket.min_zoom)) ||
(id.z >= std::ceil(styleBucket.max_zoom)) ||
(styleBucket.visibility == VisibilityType::None)) {
return;
}
auto geometryLayer = geometryTile.getLayer(styleBucket.source_layer);
if (!geometryLayer) {
@@ -114,35 +142,25 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
return;
}
std::unique_ptr<Bucket> bucket;
switch (styleBucket.type) {
case StyleLayerType::Fill:
bucket = createFillBucket(*geometryLayer, styleBucket);
createFillBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Line:
bucket = createLineBucket(*geometryLayer, styleBucket);
createLineBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Circle:
bucket = createCircleBucket(*geometryLayer, styleBucket);
createCircleBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Symbol:
bucket = createSymbolBucket(*geometryLayer, styleBucket);
createSymbolBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Raster:
return;
break;
default:
Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')",
styleBucket.name.c_str(), styleBucket.source_layer.c_str());
}
// Bucket creation might fail because the data tile may not
// contain any data that falls into this bucket.
if (!bucket)
return;
std::lock_guard<std::mutex> lock(bucketsMutex);
buckets[styleBucket.name] = std::move(bucket);
}
template <class Bucket>
@@ -161,101 +179,108 @@ void TileWorker::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& la
}
}
std::unique_ptr<Bucket> TileWorker::createFillBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
void TileWorker::createFillBucket(const GeometryTileLayer& layer,
const StyleBucket& styleBucket) {
auto bucket = std::make_unique<FillBucket>();
addBucketGeometries(bucket, layer, bucket_desc.filter);
return bucket->hasData() ? std::move(bucket) : nullptr;
// Fill does not have layout properties to apply.
addBucketGeometries(bucket, layer, styleBucket.filter);
insertBucket(styleBucket.name, std::move(bucket));
}
std::unique_ptr<Bucket> TileWorker::createLineBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
void TileWorker::createLineBucket(const GeometryTileLayer& layer,
const StyleBucket& styleBucket) {
auto bucket = std::make_unique<LineBucket>();
const float z = id.z;
auto& layout = bucket->layout;
applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, layout.cap, z);
applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, layout.join, z);
applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, layout.miter_limit, z);
applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, layout.round_limit, z);
applyLayoutProperty(PropertyKey::LineCap, styleBucket.layout, layout.cap, z);
applyLayoutProperty(PropertyKey::LineJoin, styleBucket.layout, layout.join, z);
applyLayoutProperty(PropertyKey::LineMiterLimit, styleBucket.layout, layout.miter_limit, z);
applyLayoutProperty(PropertyKey::LineRoundLimit, styleBucket.layout, layout.round_limit, z);
addBucketGeometries(bucket, layer, bucket_desc.filter);
return bucket->hasData() ? std::move(bucket) : nullptr;
addBucketGeometries(bucket, layer, styleBucket.filter);
insertBucket(styleBucket.name, std::move(bucket));
}
std::unique_ptr<Bucket> TileWorker::createCircleBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
void TileWorker::createCircleBucket(const GeometryTileLayer& layer,
const StyleBucket& styleBucket) {
auto bucket = std::make_unique<CircleBucket>();
// Circle does not have layout properties to apply.
addBucketGeometries(bucket, layer, bucket_desc.filter);
return bucket->hasData() ? std::move(bucket) : nullptr;
addBucketGeometries(bucket, layer, styleBucket.filter);
insertBucket(styleBucket.name, std::move(bucket));
}
std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
void TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
const StyleBucket& styleBucket) {
auto bucket = std::make_unique<SymbolBucket>(id.overscaling, id.z);
const float z = id.z;
auto& layout = bucket->layout;
applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, layout.placement, z);
applyLayoutProperty(PropertyKey::SymbolPlacement, styleBucket.layout, layout.placement, z);
if (layout.placement == PlacementType::Line) {
layout.icon.rotation_alignment = RotationAlignmentType::Map;
layout.text.rotation_alignment = RotationAlignmentType::Map;
};
applyLayoutProperty(PropertyKey::SymbolSpacing, bucket_desc.layout, layout.spacing, z);
applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, layout.avoid_edges, z);
applyLayoutProperty(PropertyKey::SymbolSpacing, styleBucket.layout, layout.spacing, z);
applyLayoutProperty(PropertyKey::SymbolAvoidEdges, styleBucket.layout, layout.avoid_edges, z);
applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, layout.icon.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, layout.icon.ignore_placement, z);
applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, layout.icon.optional, z);
applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, layout.icon.rotation_alignment, z);
applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, layout.icon.image, z);
applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, layout.icon.padding, z);
applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, layout.icon.rotate, z);
applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, layout.icon.keep_upright, z);
applyLayoutProperty(PropertyKey::IconOffset, bucket_desc.layout, layout.icon.offset, z);
applyLayoutProperty(PropertyKey::IconAllowOverlap, styleBucket.layout, layout.icon.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconIgnorePlacement, styleBucket.layout, layout.icon.ignore_placement, z);
applyLayoutProperty(PropertyKey::IconOptional, styleBucket.layout, layout.icon.optional, z);
applyLayoutProperty(PropertyKey::IconRotationAlignment, styleBucket.layout, layout.icon.rotation_alignment, z);
applyLayoutProperty(PropertyKey::IconImage, styleBucket.layout, layout.icon.image, z);
applyLayoutProperty(PropertyKey::IconPadding, styleBucket.layout, layout.icon.padding, z);
applyLayoutProperty(PropertyKey::IconRotate, styleBucket.layout, layout.icon.rotate, z);
applyLayoutProperty(PropertyKey::IconKeepUpright, styleBucket.layout, layout.icon.keep_upright, z);
applyLayoutProperty(PropertyKey::IconOffset, styleBucket.layout, layout.icon.offset, z);
applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, layout.text.rotation_alignment, z);
applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, layout.text.field, z);
applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, layout.text.font, z);
applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, layout.text.max_width, z);
applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, layout.text.line_height, z);
applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, layout.text.letter_spacing, z);
applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, layout.text.max_angle, z);
applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, layout.text.rotate, z);
applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, layout.text.padding, z);
applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, layout.text.ignore_placement, z);
applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, layout.text.optional, z);
applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, layout.text.justify, z);
applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, layout.text.anchor, z);
applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, layout.text.keep_upright, z);
applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, layout.text.transform, z);
applyLayoutProperty(PropertyKey::TextOffset, bucket_desc.layout, layout.text.offset, z);
applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, layout.text.allow_overlap, z);
applyLayoutProperty(PropertyKey::TextRotationAlignment, styleBucket.layout, layout.text.rotation_alignment, z);
applyLayoutProperty(PropertyKey::TextField, styleBucket.layout, layout.text.field, z);
applyLayoutProperty(PropertyKey::TextFont, styleBucket.layout, layout.text.font, z);
applyLayoutProperty(PropertyKey::TextMaxWidth, styleBucket.layout, layout.text.max_width, z);
applyLayoutProperty(PropertyKey::TextLineHeight, styleBucket.layout, layout.text.line_height, z);
applyLayoutProperty(PropertyKey::TextLetterSpacing, styleBucket.layout, layout.text.letter_spacing, z);
applyLayoutProperty(PropertyKey::TextMaxAngle, styleBucket.layout, layout.text.max_angle, z);
applyLayoutProperty(PropertyKey::TextRotate, styleBucket.layout, layout.text.rotate, z);
applyLayoutProperty(PropertyKey::TextPadding, styleBucket.layout, layout.text.padding, z);
applyLayoutProperty(PropertyKey::TextIgnorePlacement, styleBucket.layout, layout.text.ignore_placement, z);
applyLayoutProperty(PropertyKey::TextOptional, styleBucket.layout, layout.text.optional, z);
applyLayoutProperty(PropertyKey::TextJustify, styleBucket.layout, layout.text.justify, z);
applyLayoutProperty(PropertyKey::TextAnchor, styleBucket.layout, layout.text.anchor, z);
applyLayoutProperty(PropertyKey::TextKeepUpright, styleBucket.layout, layout.text.keep_upright, z);
applyLayoutProperty(PropertyKey::TextTransform, styleBucket.layout, layout.text.transform, z);
applyLayoutProperty(PropertyKey::TextOffset, styleBucket.layout, layout.text.offset, z);
applyLayoutProperty(PropertyKey::TextAllowOverlap, styleBucket.layout, layout.text.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.size, z + 1);
applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.max_size, 18);
applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.size, z + 1);
applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.max_size, 18);
applyLayoutProperty(PropertyKey::IconSize, styleBucket.layout, layout.icon.size, z + 1);
applyLayoutProperty(PropertyKey::IconSize, styleBucket.layout, layout.icon.max_size, 18);
applyLayoutProperty(PropertyKey::TextSize, styleBucket.layout, layout.text.size, z + 1);
applyLayoutProperty(PropertyKey::TextSize, styleBucket.layout, layout.text.max_size, 18);
if (bucket->needsDependencies(layer, bucket_desc.filter, *style.glyphStore, *style.sprite)) {
partialParse = true;
bucket->parseFeatures(layer, styleBucket.filter);
const bool needsDependencies = bucket->needsDependencies(*style.glyphStore, *style.sprite);
if (needsDependencies) {
// We cannot parse this bucket yet. Instead, we're saving it for later.
pending.emplace_back(styleBucket, std::move(bucket));
} else {
bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
*style.glyphAtlas, *style.glyphStore, *collisionTile);
insertBucket(styleBucket.name, std::move(bucket));
}
}
void TileWorker::insertBucket(const std::string& name, std::unique_ptr<Bucket> bucket) {
if (bucket->hasData()) {
result.buckets.emplace_back(name, std::move(bucket));
}
// We do not proceed if the parser is in a "partial" state because
// the layer ordering needs to be respected when calculating text
// collisions. Although, at this point, we requested all the resources
// needed by this tile.
if (partialParse) {
return nullptr;
}
bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas, *style.glyphAtlas,
*style.glyphStore, *collisionTile);
return bucket->hasData() ? std::move(bucket) : nullptr;
}

View File

@@ -12,6 +12,7 @@
#include <string>
#include <memory>
#include <mutex>
#include <list>
#include <unordered_map>
namespace mbgl {
@@ -24,9 +25,16 @@ class StyleLayer;
class StyleBucket;
class GeometryTileLayer;
using TileParseResult = mapbox::util::variant<
TileData::State, // success
std::string>; // error
// We're using this class to shuttle the resulting buckets from the worker thread to the MapContext
// thread. This class is movable-only because the vector contains movable-only value elements.
class TileParseResultBuckets {
public:
TileData::State state = TileData::State::invalid;
std::vector<std::pair<std::string, std::unique_ptr<Bucket>>> buckets;
};
using TileParseResult = mapbox::util::variant<TileParseResultBuckets, // success
std::string>; // error
class TileWorker : public util::noncopyable {
public:
@@ -38,20 +46,24 @@ public:
std::unique_ptr<CollisionTile>);
~TileWorker();
Bucket* getBucket(const StyleLayer&) const;
TileParseResult parse(const GeometryTile&);
void redoPlacement(float angle, float pitch, bool collisionDebug);
TileParseResult parseAllLayers(const GeometryTile&);
TileParseResult parsePendingLayers();
void redoPlacement(const std::unordered_map<std::string, std::unique_ptr<Bucket>>*,
float angle,
float pitch,
bool collisionDebug);
std::vector<util::ptr<StyleLayer>> layers;
private:
void parseLayer(const StyleLayer&, const GeometryTile&);
std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createCircleBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
void createFillBucket(const GeometryTileLayer&, const StyleBucket&);
void createLineBucket(const GeometryTileLayer&, const StyleBucket&);
void createCircleBucket(const GeometryTileLayer&, const StyleBucket&);
void createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
void insertBucket(const std::string& name, std::unique_ptr<Bucket>);
template <class Bucket>
void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
@@ -63,19 +75,16 @@ private:
Style& style;
const std::atomic<TileData::State>& state;
bool partialParse = false;
std::unique_ptr<CollisionTile> collisionTile;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added to this map 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;
// Contains buckets that we couldn't parse so far due to missing resources.
// They will be attempted on subsequent parses.
std::list<std::pair<const StyleBucket&, std::unique_ptr<Bucket>>> pending;
// Temporary holder
TileParseResultBuckets result;
};
}
} // namespace mbgl
#endif

View File

@@ -43,11 +43,12 @@ void VectorTileData::request(float pixelRatio, const std::function<void()>& call
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) {
if (res.stale) {
// Only handle fresh responses.
// Do not cancel the request here; we want to get notified about tiles that expire.
if (res.data && data == res.data) {
// We got the same data again. Abort early.
return;
}
req = nullptr;
if (res.status == Response::NotFound) {
state = State::parsed;
@@ -57,39 +58,80 @@ void VectorTileData::request(float pixelRatio, const std::function<void()>& call
if (res.status != Response::Successful) {
std::stringstream message;
message << "Failed to load [" << url << "]: " << res.message;
message << "Failed to load [" << url << "]: " << res.message;
error = message.str();
state = State::obsolete;
callback();
return;
}
state = State::loaded;
if (state == State::loading) {
state = State::loaded;
} else if (isReady()) {
state = State::partial;
}
data = res.data;
reparse(callback);
parse(callback);
});
}
bool VectorTileData::reparse(std::function<void()> callback) {
if (parsing || (state != State::loaded && state != State::partial)) {
return false;
}
parsing = true;
void VectorTileData::parse(std::function<void ()> callback) {
// Kick off a fresh parse of this tile. This happens when the tile is new, or
// when tile data changed. Replacing the workdRequest will cancel a pending work
// request in case there is one.
workRequest.reset();
workRequest = worker.parseVectorTile(tileWorker, data, [this, callback] (TileParseResult result) {
parsing = false;
workRequest.reset();
if (state == State::obsolete) {
return;
}
if (result.is<State>()) {
state = result.get<State>();
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
error = message.str();
state = State::obsolete;
}
callback();
});
}
bool VectorTileData::parsePending(std::function<void()> callback) {
if (workRequest) {
// There's already parsing or placement going on.
return false;
}
workRequest.reset();
workRequest = worker.parsePendingVectorTileLayers(tileWorker, [this, callback] (TileParseResult result) {
workRequest.reset();
if (state == State::obsolete) {
return;
}
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
error = message.str();
state = State::obsolete;
}
@@ -101,39 +143,46 @@ bool VectorTileData::reparse(std::function<void()> callback) {
}
Bucket* VectorTileData::getBucket(const StyleLayer& layer) {
if (!isReady() || !layer.bucket) {
if (!layer.bucket) {
return nullptr;
}
return tileWorker.getBucket(layer);
const auto it = buckets.find(layer.bucket->name);
if (it == buckets.end()) {
return nullptr;
}
assert(it->second);
return it->second.get();
}
void VectorTileData::redoPlacement(float angle, float pitch, bool collisionDebug) {
if (angle == currentAngle &&
pitch == currentPitch &&
collisionDebug == currentCollisionDebug)
if (angle == currentAngle && pitch == currentPitch && collisionDebug == currentCollisionDebug)
return;
lastAngle = angle;
lastPitch = pitch;
lastCollisionDebug = collisionDebug;
if (state != State::parsed || redoingPlacement)
if (workRequest) {
// Don't start a new placement request when the current one hasn't completed yet, or when
// we are parsing buckets.
return;
}
redoingPlacement = true;
currentAngle = angle;
currentPitch = pitch;
currentCollisionDebug = collisionDebug;
workRequest = worker.redoPlacement(tileWorker, angle, pitch, collisionDebug, [this] {
workRequest.reset();
workRequest = worker.redoPlacement(tileWorker, buckets, angle, pitch, collisionDebug, [this] {
workRequest.reset();
for (const auto& layer : tileWorker.layers) {
auto bucket = getBucket(*layer);
if (bucket) {
bucket->swapRenderData();
}
}
redoingPlacement = false;
redoPlacement(lastAngle, lastPitch, lastCollisionDebug);
});
}

View File

@@ -16,20 +16,16 @@ class Request;
class VectorTileData : public TileData {
public:
VectorTileData(const TileID&,
Style&,
const SourceInfo&,
float angle_,
float pitch_,
bool collisionDebug_);
VectorTileData(
const TileID&, Style&, const SourceInfo&, float angle_, float pitch_, bool collisionDebug_);
~VectorTileData();
Bucket* getBucket(const StyleLayer&) override;
void request(float pixelRatio,
const std::function<void()>& callback);
void request(float pixelRatio, const std::function<void()>& callback);
bool reparse(std::function<void ()> callback) override;
void parse(std::function<void()> callback);
bool parsePending(std::function<void()> callback) override;
void redoPlacement(float angle, float pitch, bool collisionDebug) override;
@@ -39,7 +35,11 @@ private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<WorkRequest> workRequest;
bool parsing = false;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added by tile parsing operations.
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
const SourceInfo& source;
RequestHolder req;
std::shared_ptr<const std::string> data;
@@ -49,9 +49,8 @@ private:
float currentPitch;
bool lastCollisionDebug = 0;
bool currentCollisionDebug = 0;
bool redoingPlacement = false;
};
}
} // namespace mbgl
#endif

View File

@@ -32,6 +32,8 @@ public:
virtual ~Bucket() {}
virtual bool hasData() const = 0;
inline bool needsUpload() const {
return !uploaded;
}

View File

@@ -26,7 +26,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
bool hasData() const override;
void addGeometry(const GeometryCollection&);
void drawCircles(CircleShader& shader);

View File

@@ -5,21 +5,13 @@
#include <mbgl/platform/gl.hpp>
#include <cassert>
#include <string>
using namespace mbgl;
DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)
: fontBuffer(fontBuffer_) {
}
void DebugBucket::upload() {
fontBuffer.upload();
uploaded = true;
}
void DebugBucket::render(Painter& painter, const StyleLayer&, const TileID&, const mat4& matrix) {
painter.renderDebugText(*this, matrix);
DebugBucket::DebugBucket(const TileID id, const TileData::State state_) : state(state_) {
const std::string text = std::string(id) + " - " + TileData::StateToString(state);
fontBuffer.addText(text.c_str(), 50, 200, 5);
}
void DebugBucket::drawLines(PlainShader& shader) {

View File

@@ -1,28 +1,25 @@
#ifndef MBGL_RENDERER_DEBUGBUCKET
#define MBGL_RENDERER_DEBUGBUCKET
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/map/tile_data.hpp>
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/geometry/vao.hpp>
#include <vector>
namespace mbgl {
class PlainShader;
class DebugBucket : public Bucket {
class DebugBucket : private util::noncopyable {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
DebugBucket(TileID id, TileData::State);
void drawLines(PlainShader& shader);
void drawPoints(PlainShader& shader);
const TileData::State state;
private:
DebugFontBuffer& fontBuffer;
DebugFontBuffer fontBuffer;
VertexArrayObject array;
};

View File

@@ -34,7 +34,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
bool hasData() const override;
void addGeometry(const GeometryCollection&);
void tessellate();

View File

@@ -30,7 +30,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
bool hasData() const override;
void addGeometry(const GeometryCollection&);
void addGeometry(const std::vector<Coordinate>& line);

View File

@@ -102,7 +102,7 @@ public:
// Renders the red debug frame around a tile, visualizing its perimeter.
void renderDebugFrame(const mat4 &matrix);
void renderDebugText(DebugBucket&, const mat4&);
void renderDebugText(TileData&, const mat4&);
void renderFill(FillBucket&, const FillLayer&, const TileID&, const mat4&);
void renderLine(LineBucket&, const LineLayer&, const TileID&, const mat4&);
void renderCircle(CircleBucket&, const CircleLayer&, const TileID&, const mat4&);

View File

@@ -14,34 +14,38 @@ void Painter::renderTileDebug(const Tile& tile) {
assert(tile.data);
if (data.getDebug()) {
prepareTile(tile);
renderDebugText(tile.data->debugBucket, tile.matrix);
renderDebugText(*tile.data, tile.matrix);
renderDebugFrame(tile.matrix);
}
}
void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
void Painter::renderDebugText(TileData& tileData, const mat4 &matrix) {
MBGL_DEBUG_GROUP("debug text");
config.depthTest = false;
if (!tileData.debugBucket || tileData.debugBucket->state != tileData.getState()) {
tileData.debugBucket = std::make_unique<DebugBucket>(tileData.id, tileData.getState());
}
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
// Draw white outline
plainShader->u_color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
lineWidth(4.0f * data.pixelRatio);
bucket.drawLines(*plainShader);
tileData.debugBucket->drawLines(*plainShader);
#ifndef GL_ES_VERSION_2_0
// Draw line "end caps"
MBGL_CHECK_ERROR(glPointSize(2));
bucket.drawPoints(*plainShader);
tileData.debugBucket->drawPoints(*plainShader);
#endif
// Draw black text.
plainShader->u_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
lineWidth(2.0f * data.pixelRatio);
bucket.drawLines(*plainShader);
tileData.debugBucket->drawLines(*plainShader);
config.depthTest = true;
}

View File

@@ -18,7 +18,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
bool hasData() const override;
bool setImage(std::unique_ptr<util::Image> image);

View File

@@ -90,20 +90,16 @@ bool SymbolBucket::hasIconData() const { return renderData && !renderData->icon.
bool SymbolBucket::hasCollisionBoxData() const { return renderData && !renderData->collisionBox.groups.empty(); }
bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer,
const FilterExpression& filter,
GlyphStore& glyphStore,
Sprite& sprite) {
void SymbolBucket::parseFeatures(const GeometryTileLayer& layer,
const FilterExpression& filter) {
const bool has_text = !layout.text.field.empty() && !layout.text.font.empty();
const bool has_icon = !layout.icon.image.empty();
if (!has_text && !has_icon) {
return false;
return;
}
// Determine and load glyph ranges
std::set<GlyphRange> ranges;
const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
for (GLsizei i = 0; i < featureCount; i++) {
auto feature = layer.getFeature(i);
@@ -161,12 +157,15 @@ bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer,
if (layout.placement == PlacementType::Line) {
util::mergeLines(features);
}
}
if (!glyphStore.hasGlyphRanges(layout.text.font, ranges)) {
bool SymbolBucket::needsDependencies(GlyphStore& glyphStore,
Sprite& sprite) {
if (!layout.text.field.empty() && !layout.text.font.empty() && !glyphStore.hasGlyphRanges(layout.text.font, ranges)) {
return true;
}
if (!sprite.isLoaded()) {
if (!layout.icon.image.empty() && !sprite.isLoaded()) {
return true;
}

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <map>
#include <set>
#include <vector>
namespace mbgl {
@@ -69,7 +70,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
bool hasCollisionBoxData() const;
@@ -85,10 +86,10 @@ public:
void drawIcons(IconShader& shader);
void drawCollisionBoxes(CollisionBoxShader& shader);
bool needsDependencies(const GeometryTileLayer&,
const FilterExpression&,
GlyphStore&,
Sprite&);
void parseFeatures(const GeometryTileLayer&,
const FilterExpression&);
bool needsDependencies(GlyphStore& glyphStore,
Sprite& sprite);
void placeFeatures(CollisionTile&) override;
private:
@@ -120,6 +121,7 @@ private:
const float tileExtent = 4096.0f;
const float tilePixelRatio;
std::set<GlyphRange> ranges;
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;

View File

@@ -16,7 +16,9 @@ class Worker::Impl {
public:
Impl() = default;
void parseRasterTile(RasterBucket* bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
void parseRasterTile(std::unique_ptr<RasterBucket> bucket,
const std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback) {
std::unique_ptr<util::Image> image(new util::Image(*data));
if (!(*image)) {
callback(TileParseResult("error parsing raster image"));
@@ -26,34 +28,56 @@ public:
callback(TileParseResult("error setting raster image to bucket"));
}
callback(TileParseResult(TileData::State::parsed));
TileParseResultBuckets result;
result.buckets.emplace_back("raster", std::move(bucket));
result.state = TileData::State::parsed;
callback(std::move(result));
}
void parseVectorTile(TileWorker* worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
void parseVectorTile(TileWorker* worker,
const std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback) {
try {
pbf tilePBF(reinterpret_cast<const unsigned char*>(data->data()), data->size());
callback(worker->parse(VectorTile(tilePBF)));
callback(worker->parseAllLayers(VectorTile(tilePBF)));
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void parseLiveTile(TileWorker* worker, const AnnotationTile* tile, std::function<void (TileParseResult)> callback) {
void parsePendingVectorTileLayers(TileWorker* worker,
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parse(*tile));
callback(worker->parsePendingLayers());
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void redoPlacement(TileWorker* worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
worker->redoPlacement(angle, pitch, collisionDebug);
void parseLiveTile(TileWorker* worker,
const AnnotationTile* tile,
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parseAllLayers(*tile));
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void redoPlacement(TileWorker* worker,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
float angle,
float pitch,
bool collisionDebug,
std::function<void()> callback) {
worker->redoPlacement(buckets, angle, pitch, collisionDebug);
callback();
}
};
Worker::Worker(std::size_t count) {
util::ThreadContext context = {"Worker", util::ThreadType::Worker, util::ThreadPriority::Low};
util::ThreadContext context = { "Worker", util::ThreadType::Worker, util::ThreadPriority::Low };
for (std::size_t i = 0; i < count; i++) {
threads.emplace_back(std::make_unique<util::Thread<Impl>>(context));
}
@@ -61,24 +85,50 @@ Worker::Worker(std::size_t count) {
Worker::~Worker() = default;
std::unique_ptr<WorkRequest> Worker::parseRasterTile(RasterBucket& bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
std::unique_ptr<WorkRequest>
Worker::parseRasterTile(std::unique_ptr<RasterBucket> bucket,
const std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, &bucket, data);
return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, bucket,
data);
}
std::unique_ptr<WorkRequest> Worker::parseVectorTile(TileWorker& worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
std::unique_ptr<WorkRequest>
Worker::parseVectorTile(TileWorker& worker,
const std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker, data);
return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker,
data);
}
std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker, const AnnotationTile& tile, std::function<void (TileParseResult)> callback) {
std::unique_ptr<WorkRequest>
Worker::parsePendingVectorTileLayers(TileWorker& worker,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, &tile);
return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingVectorTileLayers,
callback, &worker);
}
std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker,
const AnnotationTile& tile,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, pitch, collisionDebug);
return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker,
&tile);
}
std::unique_ptr<WorkRequest>
Worker::redoPlacement(TileWorker& worker,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>& buckets,
float angle,
float pitch,
bool collisionDebug,
std::function<void()> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker,
&buckets, angle, pitch, collisionDebug);
}
} // end namespace mbgl

View File

@@ -31,34 +31,33 @@ public:
using Request = std::unique_ptr<WorkRequest>;
Request parseRasterTile(
RasterBucket&,
std::shared_ptr<const std::string> data,
std::function<void (TileParseResult)> callback);
Request parseRasterTile(std::unique_ptr<RasterBucket> bucket,
std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback);
Request parseVectorTile(
TileWorker&,
std::shared_ptr<const std::string> data,
std::function<void (TileParseResult)> callback);
Request parseVectorTile(TileWorker&,
std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback);
Request parseLiveTile(
TileWorker&,
const AnnotationTile&,
std::function<void (TileParseResult)> callback);
Request parsePendingVectorTileLayers(TileWorker&,
std::function<void(TileParseResult)> callback);
Request redoPlacement(
TileWorker&,
float angle,
float pitch,
bool collisionDebug,
std::function<void ()> callback);
Request parseLiveTile(TileWorker&,
const AnnotationTile&,
std::function<void(TileParseResult)> callback);
Request redoPlacement(TileWorker&,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>&,
float angle,
float pitch,
bool collisionDebug,
std::function<void()> callback);
private:
class Impl;
std::vector<std::unique_ptr<util::Thread<Impl>>> threads;
std::size_t current = 0;
};
}
#endif

View File

@@ -38,6 +38,10 @@ public:
}
~MockMapContext() {
cleanup();
}
void cleanup() {
style_.reset();
}
@@ -118,6 +122,7 @@ void runTestCase(MockFileSource::Type type,
// Needed because it will make the Map thread
// join and cease logging after this point.
context->invoke(&MockMapContext::cleanup);
context.reset();
uint32_t match = 0;