@@ -73,17 +73,17 @@ GeometryTileWorker::~GeometryTileWorker() = default;
|
||||
v v
|
||||
(do parse or symbol layout; self-send "coalesced"; goto [coalescing])
|
||||
|
||||
The idea is that in the [idle] state, parsing happens immediately in response to
|
||||
a "set" message, and symbol layout happens once all symbol dependencies are met.
|
||||
During this processing, multiple "set" messages might get queued in the mailbox.
|
||||
At the end of processing, we self-send "coalesced", read all the queued messages
|
||||
until we get to "coalesced", and then re-parse if there were one or more "set"s or
|
||||
return to the [idle] state if not.
|
||||
|
||||
One important goal of the design is to prevent starvation. Under heavy load new
|
||||
requests for tiles should not prevent in progress request from completing.
|
||||
It is nevertheless possible to restart an in-progress request:
|
||||
|
||||
The idea is that in the [idle] state, parsing happens immediately in response
|
||||
to a "set" message, and symbol layout happens once all symbol dependencies
|
||||
are met. During this processing, multiple "set" messages might get queued in
|
||||
the mailbox. At the end of processing, we self-send "coalesced", read all the
|
||||
queued messages until we get to "coalesced", and then re-parse if there were
|
||||
one or more "set"s or return to the [idle] state if not.
|
||||
|
||||
One important goal of the design is to prevent starvation. Under heavy load
|
||||
new requests for tiles should not prevent in progress request from
|
||||
completing. It is nevertheless possible to restart an in-progress request:
|
||||
|
||||
- [Idle] setData -> parse()
|
||||
sends getGlyphs, hasPendingDependencies() is true
|
||||
enters [Coalescing], sends coalesced
|
||||
@@ -96,24 +96,25 @@ GeometryTileWorker::~GeometryTileWorker() = default;
|
||||
- [NeedsSymbolLayout] coalesced -> finalizeLayout()
|
||||
Generates result depending on whether dependencies are met
|
||||
-> [Idle]
|
||||
|
||||
In this situation, we are counting on the idea that even with rapid changes to
|
||||
the tile's data, the set of glyphs/images it requires will not keep growing without
|
||||
limit.
|
||||
|
||||
Although parsing (which populates all non-symbol buckets and requests dependencies
|
||||
for symbol buckets) is internally separate from symbol layout, we only return
|
||||
results to the foreground when we have completed both steps. Because we _move_
|
||||
the result buckets to the foreground, it is necessary to re-generate all buckets from
|
||||
scratch for `setShowCollisionBoxes`, even though it only affects symbol layers.
|
||||
|
||||
|
||||
In this situation, we are counting on the idea that even with rapid changes
|
||||
to the tile's data, the set of glyphs/images it requires will not keep
|
||||
growing without limit.
|
||||
|
||||
Although parsing (which populates all non-symbol buckets and requests
|
||||
dependencies for symbol buckets) is internally separate from symbol layout,
|
||||
we only return results to the foreground when we have completed both steps.
|
||||
Because we _move_ the result buckets to the foreground, it is necessary to
|
||||
re-generate all buckets from scratch for `setShowCollisionBoxes`, even though
|
||||
it only affects symbol layers.
|
||||
|
||||
The GL JS equivalent (in worker_tile.js and vector_tile_worker_source.js)
|
||||
is somewhat simpler because it relies on getGlyphs/getImages calls that transfer
|
||||
an entire set of glyphs/images on every tile load, while the native logic
|
||||
maintains a local state that can be incrementally updated. Because each tile load
|
||||
call becomes self-contained, the equivalent of the coalescing logic is handled by
|
||||
'reloadTile' queueing a single extra 'reloadTile' callback to run after the next
|
||||
completed parse.
|
||||
is somewhat simpler because it relies on getGlyphs/getImages calls that
|
||||
transfer an entire set of glyphs/images on every tile load, while the native
|
||||
logic maintains a local state that can be incrementally updated. Because each
|
||||
tile load call becomes self-contained, the equivalent of the coalescing logic
|
||||
is handled by 'reloadTile' queueing a single extra 'reloadTile' callback to
|
||||
run after the next completed parse.
|
||||
*/
|
||||
|
||||
void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
|
||||
@@ -125,16 +126,16 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
|
||||
availableImages = std::move(availableImages_);
|
||||
|
||||
switch (state) {
|
||||
case Idle:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
case Idle:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
|
||||
case Coalescing:
|
||||
case NeedsParse:
|
||||
case NeedsSymbolLayout:
|
||||
state = NeedsParse;
|
||||
break;
|
||||
case Coalescing:
|
||||
case NeedsParse:
|
||||
case NeedsSymbolLayout:
|
||||
state = NeedsParse;
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
|
||||
@@ -150,18 +151,18 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<LayerProperties>> layer
|
||||
availableImages = std::move(availableImages_);
|
||||
|
||||
switch (state) {
|
||||
case Idle:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
case Idle:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
|
||||
case Coalescing:
|
||||
case NeedsSymbolLayout:
|
||||
state = NeedsParse;
|
||||
break;
|
||||
case Coalescing:
|
||||
case NeedsSymbolLayout:
|
||||
state = NeedsParse;
|
||||
break;
|
||||
|
||||
case NeedsParse:
|
||||
break;
|
||||
case NeedsParse:
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
|
||||
@@ -190,22 +191,22 @@ void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_
|
||||
correlationID = correlationID_;
|
||||
|
||||
switch (state) {
|
||||
case Idle:
|
||||
if (!hasPendingParseResult()) {
|
||||
// Trigger parse if nothing is in flight, otherwise symbol layout will automatically
|
||||
// pick up the change
|
||||
parse();
|
||||
coalesce();
|
||||
}
|
||||
break;
|
||||
case Idle:
|
||||
if (!hasPendingParseResult()) {
|
||||
// Trigger parse if nothing is in flight, otherwise symbol
|
||||
// layout will automatically pick up the change
|
||||
parse();
|
||||
coalesce();
|
||||
}
|
||||
break;
|
||||
|
||||
case Coalescing:
|
||||
state = NeedsSymbolLayout;
|
||||
break;
|
||||
case Coalescing:
|
||||
state = NeedsSymbolLayout;
|
||||
break;
|
||||
|
||||
case NeedsSymbolLayout:
|
||||
case NeedsParse:
|
||||
break;
|
||||
case NeedsSymbolLayout:
|
||||
case NeedsParse:
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
|
||||
@@ -215,25 +216,25 @@ void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_
|
||||
void GeometryTileWorker::symbolDependenciesChanged() {
|
||||
try {
|
||||
switch (state) {
|
||||
case Idle:
|
||||
if (!layouts.empty()) {
|
||||
// Layouts are created only by parsing and the parse result can only be
|
||||
// cleared by performLayout, which also clears the layouts.
|
||||
assert(hasPendingParseResult());
|
||||
finalizeLayout();
|
||||
coalesce();
|
||||
}
|
||||
break;
|
||||
case Idle:
|
||||
if (!layouts.empty()) {
|
||||
// Layouts are created only by parsing and the parse result can only be
|
||||
// cleared by performLayout, which also clears the layouts.
|
||||
assert(hasPendingParseResult());
|
||||
finalizeLayout();
|
||||
coalesce();
|
||||
}
|
||||
break;
|
||||
|
||||
case Coalescing:
|
||||
if (!layouts.empty()) {
|
||||
state = NeedsSymbolLayout;
|
||||
}
|
||||
break;
|
||||
case Coalescing:
|
||||
if (!layouts.empty()) {
|
||||
state = NeedsSymbolLayout;
|
||||
}
|
||||
break;
|
||||
|
||||
case NeedsSymbolLayout:
|
||||
case NeedsParse:
|
||||
break;
|
||||
case NeedsSymbolLayout:
|
||||
case NeedsParse:
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
|
||||
@@ -243,26 +244,26 @@ void GeometryTileWorker::symbolDependenciesChanged() {
|
||||
void GeometryTileWorker::coalesced() {
|
||||
try {
|
||||
switch (state) {
|
||||
case Idle:
|
||||
assert(false);
|
||||
break;
|
||||
case Idle:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case Coalescing:
|
||||
state = Idle;
|
||||
break;
|
||||
case Coalescing:
|
||||
state = Idle;
|
||||
break;
|
||||
|
||||
case NeedsParse:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
case NeedsParse:
|
||||
parse();
|
||||
coalesce();
|
||||
break;
|
||||
|
||||
case NeedsSymbolLayout:
|
||||
// We may have entered NeedsSymbolLayout while coalescing
|
||||
// after a performLayout. In that case, we need to
|
||||
// start over with parsing in order to do another layout.
|
||||
hasPendingParseResult() ? finalizeLayout() : parse();
|
||||
coalesce();
|
||||
break;
|
||||
case NeedsSymbolLayout:
|
||||
// We may have entered NeedsSymbolLayout while coalescing
|
||||
// after a performLayout. In that case, we need to
|
||||
// start over with parsing in order to do another layout.
|
||||
hasPendingParseResult() ? finalizeLayout() : parse();
|
||||
coalesce();
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
|
||||
@@ -300,7 +301,10 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) {
|
||||
symbolDependenciesChanged();
|
||||
}
|
||||
|
||||
void GeometryTileWorker::onImagesAvailable(ImageMap newIconMap, ImageMap newPatternMap, ImageVersionMap newVersionMap, uint64_t imageCorrelationID_) {
|
||||
void GeometryTileWorker::onImagesAvailable(ImageMap newIconMap,
|
||||
ImageMap newPatternMap,
|
||||
ImageVersionMap newVersionMap,
|
||||
uint64_t imageCorrelationID_) {
|
||||
if (imageCorrelationID != imageCorrelationID_) {
|
||||
return; // Ignore outdated image request replies.
|
||||
}
|
||||
@@ -367,7 +371,7 @@ void GeometryTileWorker::parse() {
|
||||
}
|
||||
|
||||
const style::Layer::Impl& leaderImpl = *(group.at(0)->baseImpl);
|
||||
BucketParameters parameters { id, mode, pixelRatio, leaderImpl.getTypeInfo() };
|
||||
BucketParameters parameters{id, mode, pixelRatio, leaderImpl.getTypeInfo()};
|
||||
|
||||
auto geometryLayer = (*data)->getLayer(leaderImpl.sourceLayer);
|
||||
if (!geometryLayer) {
|
||||
@@ -381,10 +385,12 @@ void GeometryTileWorker::parse() {
|
||||
|
||||
featureIndex->setBucketLayerIDs(leaderImpl.id, layerIDs);
|
||||
|
||||
// Symbol layers and layers that support pattern properties have an extra step at layout time to figure out what images/glyphs
|
||||
// are needed to render the layer. They use the intermediate Layout data structure to accomplish this,
|
||||
// and either immediately create a bucket if no images/glyphs are used, or the Layout is stored until
|
||||
// the images/glyphs are available to add the features to the buckets.
|
||||
// Symbol layers and layers that support pattern properties have an
|
||||
// extra step at layout time to figure out what images/glyphs are needed
|
||||
// to render the layer. They use the intermediate Layout data structure
|
||||
// to accomplish this, and either immediately create a bucket if no
|
||||
// images/glyphs are used, or the Layout is stored until the
|
||||
// images/glyphs are available to add the features to the buckets.
|
||||
if (leaderImpl.getTypeInfo()->layout == LayerTypeInfo::Layout::Required) {
|
||||
std::unique_ptr<Layout> layout = LayerManager::get()->createLayout(
|
||||
{parameters, glyphDependencies, imageDependencies, availableImages}, std::move(geometryLayer), group);
|
||||
@@ -424,10 +430,10 @@ void GeometryTileWorker::parse() {
|
||||
requestNewImages(imageDependencies);
|
||||
|
||||
MBGL_TIMING_FINISH(watch,
|
||||
" Action: " << "Parsing," <<
|
||||
" SourceID: " << sourceID.c_str() <<
|
||||
" Canonical: " << static_cast<int>(id.canonical.z) << "/" << id.canonical.x << "/" << id.canonical.y <<
|
||||
" Time");
|
||||
" Action: "
|
||||
<< "Parsing,"
|
||||
<< " SourceID: " << sourceID.c_str() << " Canonical: " << static_cast<int>(id.canonical.z)
|
||||
<< "/" << id.canonical.x << "/" << id.canonical.y << " Time");
|
||||
finalizeLayout();
|
||||
}
|
||||
|
||||
@@ -439,7 +445,7 @@ bool GeometryTileWorker::hasPendingDependencies() const {
|
||||
}
|
||||
return !pendingImageDependencies.empty();
|
||||
}
|
||||
|
||||
|
||||
bool GeometryTileWorker::hasPendingParseResult() const {
|
||||
return bool(featureIndex);
|
||||
}
|
||||
@@ -448,7 +454,7 @@ void GeometryTileWorker::finalizeLayout() {
|
||||
if (!data || !layers || !hasPendingParseResult() || hasPendingDependencies()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MBGL_TIMING_START(watch)
|
||||
std::optional<AlphaImage> glyphAtlasImage;
|
||||
ImageAtlas iconAtlas = makeImageAtlas(imageMap, patternMap, versionMap);
|
||||
@@ -476,19 +482,17 @@ void GeometryTileWorker::finalizeLayout() {
|
||||
layouts.clear();
|
||||
|
||||
firstLoad = false;
|
||||
|
||||
MBGL_TIMING_FINISH(watch,
|
||||
" Action: " << "SymbolLayout," <<
|
||||
" SourceID: " << sourceID.c_str() <<
|
||||
" Canonical: " << static_cast<int>(id.canonical.z) << "/" << id.canonical.x << "/" << id.canonical.y <<
|
||||
" Time");
|
||||
|
||||
parent.invoke(&GeometryTile::onLayout, std::make_shared<GeometryTile::LayoutResult>(
|
||||
std::move(renderData),
|
||||
std::move(featureIndex),
|
||||
std::move(glyphAtlasImage),
|
||||
std::move(iconAtlas)
|
||||
), correlationID);
|
||||
MBGL_TIMING_FINISH(watch,
|
||||
" Action: "
|
||||
<< "SymbolLayout,"
|
||||
<< " SourceID: " << sourceID.c_str() << " Canonical: " << static_cast<int>(id.canonical.z)
|
||||
<< "/" << id.canonical.x << "/" << id.canonical.y << " Time");
|
||||
|
||||
parent.invoke(&GeometryTile::onLayout,
|
||||
std::make_shared<GeometryTile::LayoutResult>(
|
||||
std::move(renderData), std::move(featureIndex), std::move(glyphAtlasImage), std::move(iconAtlas)),
|
||||
correlationID);
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
Reference in New Issue
Block a user