diff options
Diffstat (limited to 'src/mbgl/tile')
-rw-r--r-- | src/mbgl/tile/custom_geometry_tile.cpp | 91 | ||||
-rw-r--r-- | src/mbgl/tile/custom_geometry_tile.hpp | 44 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile.cpp | 94 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile.hpp | 35 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile_worker.cpp | 39 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile_worker.hpp | 12 | ||||
-rw-r--r-- | src/mbgl/tile/raster_dem_tile.cpp | 125 | ||||
-rw-r--r-- | src/mbgl/tile/raster_dem_tile.hpp | 105 | ||||
-rw-r--r-- | src/mbgl/tile/raster_dem_tile_worker.cpp | 27 | ||||
-rw-r--r-- | src/mbgl/tile/raster_dem_tile_worker.hpp | 23 | ||||
-rw-r--r-- | src/mbgl/tile/raster_tile.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/tile/raster_tile.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/tile/tile.cpp | 6 | ||||
-rw-r--r-- | src/mbgl/tile/tile.hpp | 33 | ||||
-rw-r--r-- | src/mbgl/tile/tile_cache.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/tile/tile_cache.hpp | 3 |
16 files changed, 572 insertions, 85 deletions
diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp new file mode 100644 index 0000000000..33962ad87d --- /dev/null +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -0,0 +1,91 @@ +#include <mbgl/tile/custom_geometry_tile.hpp> +#include <mbgl/tile/geojson_tile_data.hpp> +#include <mbgl/renderer/query.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/style/filter_evaluator.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/tile/tile_observer.hpp> +#include <mbgl/style/custom_tile_loader.hpp> + +#include <mapbox/geojsonvt.hpp> + +namespace mbgl { + +CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID, + std::string sourceID_, + const TileParameters& parameters, + const style::CustomGeometrySource::TileOptions options_, + ActorRef<style::CustomTileLoader> loader_) + : GeometryTile(overscaledTileID, sourceID_, parameters), + necessity(TileNecessity::Optional), + options(options_), + loader(loader_), + mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), + actorRef(*this, mailbox) { +} + +CustomGeometryTile::~CustomGeometryTile() { + loader.invoke(&style::CustomTileLoader::removeTile, id); +} + +void CustomGeometryTile::setTileData(const GeoJSON& geoJSON) { + + auto featureData = mapbox::geometry::feature_collection<int16_t>(); + if (geoJSON.is<FeatureCollection>() && !geoJSON.get<FeatureCollection>().empty()) { + const double scale = util::EXTENT / options.tileSize; + + mapbox::geojsonvt::TileOptions vtOptions; + vtOptions.extent = util::EXTENT; + vtOptions.buffer = ::round(scale * options.buffer); + vtOptions.tolerance = scale * options.tolerance; + featureData = mapbox::geojsonvt::geoJSONToTile(geoJSON, id.canonical.z, id.canonical.x, id.canonical.y, vtOptions, options.wrap, options.clip).features; + } else { + setNecessity(TileNecessity::Optional); + } + setData(std::make_unique<GeoJSONTileData>(std::move(featureData))); +} + +void CustomGeometryTile::invalidateTileData() { + stale = true; + observer->onTileChanged(*this); +} + +//Fetching tile data for custom sources is assumed to be an expensive operation. +// Only required tiles make fetchTile requests. Attempt to cancel a tile +// that is no longer required. +void CustomGeometryTile::setNecessity(TileNecessity newNecessity) { + if (newNecessity != necessity || stale ) { + necessity = newNecessity; + if (necessity == TileNecessity::Required) { + loader.invoke(&style::CustomTileLoader::fetchTile, id, actorRef); + stale = false; + } else if (!isRenderable()) { + loader.invoke(&style::CustomTileLoader::cancelTile, id); + } + } +} + +void CustomGeometryTile::querySourceFeatures( + std::vector<Feature>& result, + const SourceQueryOptions& queryOptions) { + + // Ignore the sourceLayer, there is only one + auto layer = getData()->getLayer({}); + + if (layer) { + auto featureCount = layer->featureCount(); + for (std::size_t i = 0; i < featureCount; i++) { + auto feature = layer->getFeature(i); + + // Apply filter, if any + if (queryOptions.filter && !(*queryOptions.filter)(*feature)) { + continue; + } + + result.push_back(convertFeature(*feature, id.canonical)); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.hpp b/src/mbgl/tile/custom_geometry_tile.hpp new file mode 100644 index 0000000000..1df44e6b2a --- /dev/null +++ b/src/mbgl/tile/custom_geometry_tile.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/util/feature.hpp> +#include <mbgl/util/geojson.hpp> +#include <mbgl/actor/mailbox.hpp> + +namespace mbgl { + +class TileParameters; + +namespace style { +class CustomTileLoader; +} // namespace style + +class CustomGeometryTile: public GeometryTile { +public: + CustomGeometryTile(const OverscaledTileID&, + std::string sourceID, + const TileParameters&, + const style::CustomGeometrySource::TileOptions, + ActorRef<style::CustomTileLoader> loader); + ~CustomGeometryTile() override; + + void setTileData(const GeoJSON& data); + void invalidateTileData(); + + void setNecessity(TileNecessity) final; + + void querySourceFeatures( + std::vector<Feature>& result, + const SourceQueryOptions&) override; + +private: + bool stale = true; + TileNecessity necessity; + const style::CustomGeometrySource::TileOptions options; + ActorRef<style::CustomTileLoader> loader; + std::shared_ptr<Mailbox> mailbox; + ActorRef<CustomGeometryTile> actorRef; +}; + +} // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 8c018ce3aa..a58c744065 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -15,7 +15,6 @@ #include <mbgl/renderer/image_atlas.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/geometry/feature_index.hpp> -#include <mbgl/text/collision_tile.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/filter_evaluator.hpp> #include <mbgl/util/logging.hpp> @@ -33,7 +32,7 @@ using namespace style; GeometryTile's 'correlationID' is used for ensuring the tile will be flagged as non-pending only when the placement coming from the last operation (as in - 'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for + 'setData', 'setLayers', 'setShowCollisionBoxes') occurs. This is important for still mode rendering as we want to render only when all layout and placement operations are completed. @@ -52,13 +51,15 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, worker(parameters.workerScheduler, ActorRef<GeometryTile>(*this, mailbox), id_, + sourceID, obsolete, parameters.mode, - parameters.pixelRatio), + parameters.pixelRatio, + parameters.debugOptions & MapDebugOptions::Collision), glyphManager(parameters.glyphManager), imageManager(parameters.imageManager), - lastYStretch(1.0f), - mode(parameters.mode) { + mode(parameters.mode), + showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) { } GeometryTile::~GeometryTile() { @@ -89,25 +90,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) { worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID); } -void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { - if (requestedConfig == desiredConfig) { - return; - } - - // Mark the tile as pending again if it was complete before to prevent signaling a complete - // state despite pending parse operations. - pending = true; - - ++correlationID; - requestedConfig = desiredConfig; - invokePlacement(); -} - -void GeometryTile::invokePlacement() { - if (requestedConfig) { - worker.invoke(&GeometryTileWorker::setPlacementConfig, *requestedConfig, correlationID); - } -} void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) { // Mark the tile as pending again if it was complete before to prevent signaling a complete @@ -134,14 +116,22 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID); } +void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) { + if (showCollisionBoxes != showCollisionBoxes_) { + showCollisionBoxes = showCollisionBoxes_; + ++correlationID; + worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID); + } +} + void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { - loaded = true; - renderable = true; + // Don't mark ourselves loaded or renderable until the first successful placement + // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't + // replacing a tile at a different zoom that _did_ have symbols. (void)resultCorrelationID; nonSymbolBuckets = std::move(result.nonSymbolBuckets); - featureIndex = std::move(result.featureIndex); - data = std::move(result.tileData); - collisionTile.reset(); + pendingFeatureIndex = std::move(result.featureIndex); + pendingData = std::move(result.tileData); observer->onTileChanged(*this); } @@ -152,16 +142,13 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr pending = false; } symbolBuckets = std::move(result.symbolBuckets); - collisionTile = std::move(result.collisionTile); if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); } if (result.iconAtlasImage) { iconAtlasImage = std::move(*result.iconAtlasImage); } - if (collisionTile.get()) { - lastYStretch = collisionTile->yStretch; - } + observer->onTileChanged(*this); } @@ -226,12 +213,22 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { return it->second.get(); } +void GeometryTile::commitFeatureIndex() { + if (pendingFeatureIndex) { + featureIndex = std::move(pendingFeatureIndex); + } + if (pendingData) { + data = std::move(pendingData); + } +} + void GeometryTile::queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState& transformState, const std::vector<const RenderLayer*>& layers, - const RenderedQueryOptions& options) { + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) { if (!featureIndex || !data) return; @@ -251,9 +248,10 @@ void GeometryTile::queryRenderedFeatures( std::pow(2, transformState.getZoom() - id.overscaledZ), options, *data, - id.canonical, + id.toUnwrapped(), + sourceID, layers, - collisionTile.get(), + collisionIndex, additionalRadius); } @@ -293,11 +291,25 @@ void GeometryTile::querySourceFeatures( } } -float GeometryTile::yStretch() const { - // collisionTile gets reset in onLayout but we don't clear the symbolBuckets - // until a new placement result comes along, so keep the yStretch value in - // case we need to render them. - return lastYStretch; +bool GeometryTile::holdForFade() const { + return mode == MapMode::Continuous && + (fadeState == FadeState::NeedsFirstPlacement || fadeState == FadeState::NeedsSecondPlacement); +} + +void GeometryTile::markRenderedIdeal() { + fadeState = FadeState::Loaded; +} +void GeometryTile::markRenderedPreviously() { + if (fadeState == FadeState::Loaded) { + fadeState = FadeState::NeedsFirstPlacement; + } +} +void GeometryTile::performedFadePlacement() { + if (fadeState == FadeState::NeedsFirstPlacement) { + fadeState = FadeState::NeedsSecondPlacement; + } else if (fadeState == FadeState::NeedsSecondPlacement) { + fadeState = FadeState::CanRemove; + } } } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index a478aad504..00a4aafadf 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -4,8 +4,6 @@ #include <mbgl/tile/geometry_tile_worker.hpp> #include <mbgl/renderer/image_manager.hpp> #include <mbgl/text/glyph_manager.hpp> -#include <mbgl/text/placement_config.hpp> -#include <mbgl/text/collision_tile.hpp> #include <mbgl/util/feature.hpp> #include <mbgl/util/throttler.hpp> #include <mbgl/actor/actor.hpp> @@ -36,9 +34,9 @@ public: void setError(std::exception_ptr); void setData(std::unique_ptr<const GeometryTileData>); - void setPlacementConfig(const PlacementConfig&) override; void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override; - + void setShowCollisionBoxes(const bool showCollisionBoxes) override; + void onGlyphsAvailable(GlyphMap) override; void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override; @@ -56,7 +54,8 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector<const RenderLayer*>& layers, - const RenderedQueryOptions& options) override; + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) override; void querySourceFeatures( std::vector<Feature>& result, @@ -82,16 +81,13 @@ public: class PlacementResult { public: std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; - std::unique_ptr<CollisionTile> collisionTile; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_, - std::unique_ptr<CollisionTile> collisionTile_, optional<AlphaImage> glyphAtlasImage_, optional<PremultipliedImage> iconAtlasImage_) : symbolBuckets(std::move(symbolBuckets_)), - collisionTile(std::move(collisionTile_)), glyphAtlasImage(std::move(glyphAtlasImage_)), iconAtlasImage(std::move(iconAtlasImage_)) {} }; @@ -99,7 +95,12 @@ public: void onError(std::exception_ptr, uint64_t correlationID); - float yStretch() const override; + bool holdForFade() const override; + void markRenderedIdeal() override; + void markRenderedPreviously() override; + void performedFadePlacement() override; + + void commitFeatureIndex() override; protected: const GeometryTileData* getData() { @@ -108,7 +109,6 @@ protected: private: void markObsolete(); - void invokePlacement(); const std::string sourceID; @@ -122,21 +122,30 @@ private: ImageManager& imageManager; uint64_t correlationID = 0; - optional<PlacementConfig> requestedConfig; std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets; std::unique_ptr<FeatureIndex> featureIndex; + std::unique_ptr<FeatureIndex> pendingFeatureIndex; std::unique_ptr<const GeometryTileData> data; + std::unique_ptr<const GeometryTileData> pendingData; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; - std::unique_ptr<CollisionTile> collisionTile; - float lastYStretch; const MapMode mode; + + bool showCollisionBoxes; + + enum class FadeState { + Loaded, + NeedsFirstPlacement, + NeedsSecondPlacement, + CanRemove + }; + FadeState fadeState = FadeState::Loaded; public: optional<gl::Texture> glyphAtlasTexture; optional<gl::Texture> iconAtlasTexture; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 50429420c3..24841dd125 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -1,7 +1,6 @@ #include <mbgl/tile/geometry_tile_worker.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/tile/geometry_tile.hpp> -#include <mbgl/text/collision_tile.hpp> #include <mbgl/layout/symbol_layout.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/renderer/group_by_layout.hpp> @@ -24,20 +23,36 @@ using namespace style; GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_, ActorRef<GeometryTile> parent_, OverscaledTileID id_, + const std::string& sourceID_, const std::atomic<bool>& obsolete_, const MapMode mode_, - const float pixelRatio_) + const float pixelRatio_, + const bool showCollisionBoxes_) : self(std::move(self_)), parent(std::move(parent_)), id(std::move(id_)), + sourceID(sourceID_), obsolete(obsolete_), mode(mode_), - pixelRatio(pixelRatio_) { + pixelRatio(pixelRatio_), + showCollisionBoxes(showCollisionBoxes_) { } GeometryTileWorker::~GeometryTileWorker() = default; /* + NOTE: The comments below are technically correct, but currently + conceptually misleading. The change to foreground label placement + means that: + (1) "placement" here is a misnomer: the remaining role of + "attemptPlacement" is symbol buffer generation + (2) Once a tile has completed layout, we will only run + "attemptPlacement" once + (3) Tiles won't be rendered until "attemptPlacement" has run once + + TODO: Simplify GeometryTileWorker to fit its new role + https://github.com/mapbox/mapbox-gl-native/issues/10457 + GeometryTileWorker is a state machine. This is its transition diagram. States are indicated by [state], lines are transitions triggered by messages, (parentheses) are actions taken on transition. @@ -116,9 +131,9 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_, } } -void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, uint64_t correlationID_) { +void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_) { try { - placementConfig = std::move(placementConfig_); + showCollisionBoxes = showCollisionBoxes_; correlationID = correlationID_; switch (state) { @@ -372,7 +387,7 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const { } void GeometryTileWorker::attemptPlacement() { - if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) { + if (!data || !layers || hasPendingSymbolDependencies()) { return; } @@ -392,13 +407,13 @@ void GeometryTileWorker::attemptPlacement() { } symbolLayout->prepare(glyphMap, glyphAtlas.positions, - imageMap, imageAtlas.positions); + imageMap, imageAtlas.positions, + id, sourceID); } symbolLayoutsNeedPreparation = false; } - auto collisionTile = std::make_unique<CollisionTile>(*placementConfig); std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; for (auto& symbolLayout : symbolLayouts) { @@ -410,15 +425,19 @@ void GeometryTileWorker::attemptPlacement() { continue; } - std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile); + std::shared_ptr<SymbolBucket> bucket = symbolLayout->place(showCollisionBoxes); for (const auto& pair : symbolLayout->layerPaintProperties) { + if (!firstLoad) { + bucket->justReloaded = true; + } buckets.emplace(pair.first, bucket); } } + firstLoad = false; + parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult { std::move(buckets), - std::move(collisionTile), std::move(glyphAtlasImage), std::move(iconAtlasImage), }, correlationID); diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index 1425daa7a1..0276392679 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -4,7 +4,6 @@ #include <mbgl/tile/tile_id.hpp> #include <mbgl/style/image_impl.hpp> #include <mbgl/text/glyph.hpp> -#include <mbgl/text/placement_config.hpp> #include <mbgl/actor/actor_ref.hpp> #include <mbgl/util/optional.hpp> #include <mbgl/util/immutable.hpp> @@ -28,14 +27,16 @@ public: GeometryTileWorker(ActorRef<GeometryTileWorker> self, ActorRef<GeometryTile> parent, OverscaledTileID, + const std::string&, const std::atomic<bool>&, const MapMode, - const float pixelRatio); + const float pixelRatio, + const bool showCollisionBoxes_); ~GeometryTileWorker(); void setLayers(std::vector<Immutable<style::Layer::Impl>>, uint64_t correlationID); void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID); - void setPlacementConfig(PlacementConfig, uint64_t correlationID); + void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_); void onGlyphsAvailable(GlyphMap glyphs); void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID); @@ -57,6 +58,7 @@ private: ActorRef<GeometryTile> parent; const OverscaledTileID id; + const std::string sourceID; const std::atomic<bool>& obsolete; const MapMode mode; const float pixelRatio; @@ -75,7 +77,6 @@ private: // Outer optional indicates whether we've received it or not. optional<std::vector<Immutable<style::Layer::Impl>>> layers; optional<std::unique_ptr<const GeometryTileData>> data; - optional<PlacementConfig> placementConfig; bool symbolLayoutsNeedPreparation = false; std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts; @@ -83,6 +84,9 @@ private: ImageDependencies pendingImageDependencies; GlyphMap glyphMap; ImageMap imageMap; + + bool showCollisionBoxes; + bool firstLoad = true; }; } // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp new file mode 100644 index 0000000000..5db298cf4c --- /dev/null +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -0,0 +1,125 @@ +#include <mbgl/tile/raster_dem_tile.hpp> +#include <mbgl/tile/raster_dem_tile_worker.hpp> +#include <mbgl/tile/tile_observer.hpp> +#include <mbgl/tile/tile_loader_impl.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/actor/scheduler.hpp> + +namespace mbgl { + +RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, + const TileParameters& parameters, + const Tileset& tileset) + : Tile(id_), + loader(*this, id_, parameters, tileset), + mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), + worker(parameters.workerScheduler, + ActorRef<RasterDEMTile>(*this, mailbox)) { + + encoding = tileset.encoding; + if ( id.canonical.y == 0 ){ + // this tile doesn't have upper neighboring tiles so marked those as backfilled + neighboringTiles = neighboringTiles | DEMTileNeighbors::NoUpper; + } + + if (id.canonical.y + 1 == std::pow(2, id.canonical.z)){ + // this tile doesn't have lower neighboring tiles so marked those as backfilled + neighboringTiles = neighboringTiles | DEMTileNeighbors::NoLower; + } +} + +RasterDEMTile::~RasterDEMTile() = default; + +void RasterDEMTile::setError(std::exception_ptr err) { + loaded = true; + observer->onTileError(*this, err); +} + +void RasterDEMTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) { + modified = modified_; + expires = expires_; +} + +void RasterDEMTile::setData(std::shared_ptr<const std::string> data) { + pending = true; + ++correlationID; + worker.invoke(&RasterDEMTileWorker::parse, data, correlationID, encoding); +} + +void RasterDEMTile::onParsed(std::unique_ptr<HillshadeBucket> result, const uint64_t resultCorrelationID) { + bucket = std::move(result); + loaded = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + renderable = bucket ? true : false; + observer->onTileChanged(*this); +} + +void RasterDEMTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) { + loaded = true; + if (resultCorrelationID == correlationID) { + pending = false; + } + observer->onTileError(*this, err); +} + +void RasterDEMTile::upload(gl::Context& context) { + if (bucket) { + bucket->upload(context); + } +} + + +Bucket* RasterDEMTile::getBucket(const style::Layer::Impl&) const { + return bucket.get(); +} + +HillshadeBucket* RasterDEMTile::getBucket() const { + return bucket.get(); +} + +void RasterDEMTile::backfillBorder(const RasterDEMTile& borderTile, const DEMTileNeighbors mask) { + int32_t dx = borderTile.id.canonical.x - id.canonical.x; + const int8_t dy = borderTile.id.canonical.y - id.canonical.y; + const uint32_t dim = pow(2, id.canonical.z); + if (dx == 0 && dy == 0) return; + if (std::abs(dy) > 1) return; + // neighbor is in another world wrap + if (std::abs(dx) > 1) { + if (std::abs(int(dx + dim)) == 1) { + dx += dim; + } else if (std::abs(int(dx - dim)) == 1) { + dx -= dim; + } + } + const HillshadeBucket* borderBucket = borderTile.getBucket(); + if (borderBucket) { + const DEMData& borderDEM = borderBucket->getDEMData(); + DEMData& tileDEM = bucket->getDEMData(); + + tileDEM.backfillBorder(borderDEM, dx, dy); + // update the bitmask to indicate that this tiles have been backfilled by flipping the relevant bit + this->neighboringTiles = this->neighboringTiles | mask; + // mark HillshadeBucket.prepared as false so it runs through the prepare render pass + // with the new texture data we just backfilled + bucket->setPrepared(false); + } +} + +void RasterDEMTile::setMask(TileMask&& mask) { + if (bucket) { + bucket->setMask(std::move(mask)); + } +} + +void RasterDEMTile::setNecessity(TileNecessity necessity) { + loader.setNecessity(necessity); +} + +} // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile.hpp b/src/mbgl/tile/raster_dem_tile.hpp new file mode 100644 index 0000000000..0c8dd75961 --- /dev/null +++ b/src/mbgl/tile/raster_dem_tile.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include <mbgl/tile/tile.hpp> +#include <mbgl/tile/tile_loader.hpp> +#include <mbgl/tile/raster_dem_tile_worker.hpp> +#include <mbgl/actor/actor.hpp> + +namespace mbgl { + +class Tileset; +class TileParameters; +class HillshadeBucket; + +enum class DEMTileNeighbors : uint8_t { + // 0b00000000 + Empty = 0 << 1, + + // 0b00000001 + Left = 1 << 0, + // 0b00000010 + Right = 1 << 1, + // 0b00000100 + TopLeft = 1 << 2, + // 0b00001000 + TopCenter = 1 << 3, + // 0b00010000 + TopRight = 1 << 4, + // 0b00100000 + BottomLeft = 1 << 5, + // 0b01000000 + BottomCenter = 1 << 6, + // 0b10000000 + BottomRight = 1 << 7, + + // helper enums for tiles with no upper/lower neighbors + // and completely backfilled tiles + + // 0b00011100 + NoUpper = 0b00011100, + // 0b11100000 + NoLower = 0b11100000, + // 0b11111111 + Complete = 0b11111111 +}; + +inline DEMTileNeighbors operator|(DEMTileNeighbors a, DEMTileNeighbors b) { + return static_cast<DEMTileNeighbors>(int(a) | int(b)); +}; + +inline DEMTileNeighbors operator&(DEMTileNeighbors a, DEMTileNeighbors b) { + return static_cast<DEMTileNeighbors>(int(a) & int(b)); +} + +inline bool operator!=(DEMTileNeighbors a, DEMTileNeighbors b) { + return static_cast<unsigned char>(a) != static_cast<unsigned char>(b); +} + +namespace style { +class Layer; +} // namespace style + +class RasterDEMTile : public Tile { +public: + RasterDEMTile(const OverscaledTileID&, + const TileParameters&, + const Tileset&); + ~RasterDEMTile() override; + + void setNecessity(TileNecessity) final; + + void setError(std::exception_ptr); + void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); + void setData(std::shared_ptr<const std::string> data); + + void upload(gl::Context&) override; + Bucket* getBucket(const style::Layer::Impl&) const override; + + HillshadeBucket* getBucket() const; + void backfillBorder(const RasterDEMTile& borderTile, const DEMTileNeighbors mask); + + // neighboringTiles is a bitmask for which neighboring tiles have been backfilled + // there are max 8 possible neighboring tiles, so each bit represents one neighbor + DEMTileNeighbors neighboringTiles = DEMTileNeighbors::Empty; + + void setMask(TileMask&&) override; + + void onParsed(std::unique_ptr<HillshadeBucket> result, uint64_t correlationID); + void onError(std::exception_ptr, uint64_t correlationID); + +private: + TileLoader<RasterDEMTile> loader; + + std::shared_ptr<Mailbox> mailbox; + Actor<RasterDEMTileWorker> worker; + + uint64_t correlationID = 0; + Tileset::DEMEncoding encoding; + + // Contains the Bucket object for the tile. Buckets are render + // objects and they get added by tile parsing operations. + std::unique_ptr<HillshadeBucket> bucket; + +}; + +} // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile_worker.cpp b/src/mbgl/tile/raster_dem_tile_worker.cpp new file mode 100644 index 0000000000..7338e578c7 --- /dev/null +++ b/src/mbgl/tile/raster_dem_tile_worker.cpp @@ -0,0 +1,27 @@ +#include <mbgl/tile/raster_dem_tile_worker.hpp> +#include <mbgl/tile/raster_dem_tile.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/actor/actor.hpp> +#include <mbgl/util/premultiply.hpp> + +namespace mbgl { + +RasterDEMTileWorker::RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef<RasterDEMTile> parent_) + : parent(std::move(parent_)) { +} + +void RasterDEMTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding) { + if (!data) { + parent.invoke(&RasterDEMTile::onParsed, nullptr, correlationID); // No data; empty tile. + return; + } + + try { + auto bucket = std::make_unique<HillshadeBucket>(decodeImage(*data), encoding); + parent.invoke(&RasterDEMTile::onParsed, std::move(bucket), correlationID); + } catch (...) { + parent.invoke(&RasterDEMTile::onError, std::current_exception(), correlationID); + } +} + +} // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile_worker.hpp b/src/mbgl/tile/raster_dem_tile_worker.hpp new file mode 100644 index 0000000000..5a8222bc2d --- /dev/null +++ b/src/mbgl/tile/raster_dem_tile_worker.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/util/tileset.hpp> + +#include <memory> +#include <string> + +namespace mbgl { + +class RasterDEMTile; + +class RasterDEMTileWorker { +public: + RasterDEMTileWorker(ActorRef<RasterDEMTileWorker>, ActorRef<RasterDEMTile>); + + void parse(std::shared_ptr<const std::string> data, uint64_t correlationID, Tileset::DEMEncoding encoding); + +private: + ActorRef<RasterDEMTile> parent; +}; + +} // namespace mbgl diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index 85fcea77b7..ff23d4493e 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -24,9 +24,6 @@ RasterTile::RasterTile(const OverscaledTileID& id_, RasterTile::~RasterTile() = default; -void RasterTile::cancel() { -} - void RasterTile::setError(std::exception_ptr err) { loaded = true; observer->onTileError(*this, err); diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index 192769ed8f..e25329119a 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -20,7 +20,7 @@ public: RasterTile(const OverscaledTileID&, const TileParameters&, const Tileset&); - ~RasterTile() final; + ~RasterTile() override; void setNecessity(TileNecessity) final; @@ -28,8 +28,6 @@ public: void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires); void setData(std::shared_ptr<const std::string> data); - void cancel() override; - void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index f36a472e72..88db2ba07c 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -18,6 +18,9 @@ void Tile::setObserver(TileObserver* observer_) { observer = observer_; } +void Tile::cancel() { +} + void Tile::setTriedCache() { triedOptional = true; observer->onTileChanged(*this); @@ -34,7 +37,8 @@ void Tile::queryRenderedFeatures( const GeometryCoordinates&, const TransformState&, const std::vector<const RenderLayer*>&, - const RenderedQueryOptions&) {} + const RenderedQueryOptions&, + const CollisionIndex&) {} void Tile::querySourceFeatures( std::vector<Feature>&, diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 8be7c4d862..23365c6ae3 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -23,11 +23,12 @@ namespace mbgl { class DebugBucket; class TransformState; class TileObserver; -class PlacementConfig; class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; +class CollisionIndex; + namespace gl { class Context; } // namespace gl @@ -42,12 +43,12 @@ public: virtual void setNecessity(TileNecessity) {} // Mark this tile as no longer needed and cancel any pending work. - virtual void cancel() = 0; + virtual void cancel(); virtual void upload(gl::Context&) = 0; virtual Bucket* getBucket(const style::Layer::Impl&) const = 0; - virtual void setPlacementConfig(const PlacementConfig&) {} + virtual void setShowCollisionBoxes(const bool) {} virtual void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) {} virtual void setMask(TileMask&&) {} @@ -56,7 +57,8 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector<const RenderLayer*>&, - const RenderedQueryOptions& options); + const RenderedQueryOptions& options, + const CollisionIndex&); virtual void querySourceFeatures( std::vector<Feature>& result, @@ -92,7 +94,26 @@ public: bool isComplete() const { return loaded && !pending; } - + + // "holdForFade" is used to keep tiles in the render tree after they're no longer + // ideal tiles in order to allow symbols to fade out + virtual bool holdForFade() const { + return false; + } + // Set whenever this tile is used as an ideal tile + virtual void markRenderedIdeal() {} + // Set when the tile is removed from the ideal render set but may still be held for fading + virtual void markRenderedPreviously() {} + // Placement operation performed while this tile is fading + // We hold onto a tile for two placements: fading starts with the first placement + // and will have time to finish by the second placement. + virtual void performedFadePlacement() {} + + // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex + // generated from the same data. Calling commitFeatureIndex signals the current + // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex + virtual void commitFeatureIndex() {} + void dumpDebugLogs() const; const OverscaledTileID id; @@ -101,8 +122,6 @@ public: // Contains the tile ID string for painting debug information. std::unique_ptr<DebugBucket> debugBucket; - - virtual float yStretch() const { return 1.0f; } protected: bool triedOptional = false; diff --git a/src/mbgl/tile/tile_cache.cpp b/src/mbgl/tile/tile_cache.cpp index 3fafb1259c..463d397608 100644 --- a/src/mbgl/tile/tile_cache.cpp +++ b/src/mbgl/tile/tile_cache.cpp @@ -33,13 +33,22 @@ void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile> tile) { // purge oldest key/tile if necessary if (orderedKeys.size() > size) { - get(orderedKeys.front()); + pop(orderedKeys.front()); } assert(orderedKeys.size() <= size); } -std::unique_ptr<Tile> TileCache::get(const OverscaledTileID& key) { +Tile* TileCache::get(const OverscaledTileID& key) { + auto it = tiles.find(key); + if (it != tiles.end()) { + return it->second.get(); + } else { + return nullptr; + } +} + +std::unique_ptr<Tile> TileCache::pop(const OverscaledTileID& key) { std::unique_ptr<Tile> tile; diff --git a/src/mbgl/tile/tile_cache.hpp b/src/mbgl/tile/tile_cache.hpp index 80fe98a20c..88358b8cdc 100644 --- a/src/mbgl/tile/tile_cache.hpp +++ b/src/mbgl/tile/tile_cache.hpp @@ -17,7 +17,8 @@ public: void setSize(size_t); size_t getSize() const { return size; }; void add(const OverscaledTileID& key, std::unique_ptr<Tile> data); - std::unique_ptr<Tile> get(const OverscaledTileID& key); + std::unique_ptr<Tile> pop(const OverscaledTileID& key); + Tile* get(const OverscaledTileID& key); bool has(const OverscaledTileID& key); void clear(); |