diff --git a/.gitmodules b/.gitmodules index 9bd3c4264..1f4c38389 100644 --- a/.gitmodules +++ b/.gitmodules @@ -103,7 +103,7 @@ [submodule "vendor/maplibre-tile-spec"] path = vendor/maplibre-tile-spec url = https://github.com/maplibre/maplibre-tile-spec.git - branch = cpp + branch = main [submodule "vendor/kdbush.hpp"] path = vendor/kdbush.hpp url = https://github.com/mourner/kdbush.hpp.git diff --git a/BUILD.bazel b/BUILD.bazel index 7e9acdadf..d95df36e5 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -154,7 +154,7 @@ cc_library( "//vendor:unordered_dense", "//vendor:vector-tile", "//vendor:wagyu", - "//vendor/maplibre-tile-spec/cpp:mlt_cpp", + "@maplibre_tile_spec//cpp:mlt_cpp", ] + select({ "@platforms//os:ios": [ "//vendor:ghc_filesystem", diff --git a/MODULE.bazel b/MODULE.bazel index a24413a81..c1fec79af 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -13,6 +13,12 @@ bazel_dep(name = "rules_cc", version = "0.2.14") bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "rules_swift_package_manager", version = "1.3.0") bazel_dep(name = "googletest", version = "1.17.0.bcr.2") +bazel_dep(name = "maplibre-tile-spec", version = "0.0.0", repo_name = "maplibre_tile_spec") + +local_path_override( + module_name = "maplibre-tile-spec", + path = "vendor/maplibre-tile-spec", +) # rules_xcodeproj accidentally includes a trailing comma in the generated # Testing.framework rsync exclusion, causing intermittent test-host build races. diff --git a/include/mbgl/map/map_options.hpp b/include/mbgl/map/map_options.hpp index 12a6b874f..cc8e5bfb2 100644 --- a/include/mbgl/map/map_options.hpp +++ b/include/mbgl/map/map_options.hpp @@ -133,6 +133,22 @@ public: */ float pixelRatio() const; + /** + * @brief Sets whether to enable decoding of MapLibre Tile (MLT) tiles with FastPFOR integer encodings. + * By default, it is set to false. Such tiles will fail if not explicitly enabled. + * + * @param enable true to enable, false to disable + * @return MapOptions for chaining options together. + */ + MapOptions& withFastPFOREnabled(bool enable); + + /** + * @brief Gets the previously set (or default) value. + * + * @return true if enabled, alse otherwise. + */ + bool fastPFOREnabled() const; + private: class Impl; std::unique_ptr impl_; diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_options.cpp b/platform/android/MapLibreAndroid/src/cpp/native_map_options.cpp index ad217c729..cb7ca732c 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_options.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_options.cpp @@ -47,5 +47,11 @@ bool NativeMapOptions::asyncRendererCleanup(jni::JNIEnv &env, const jni::Object< return obj.Get(env, asyncRendererCleanupField); } +bool NativeMapOptions::fastPFOREnabled(jni::JNIEnv &env, const jni::Object &obj) { + auto &javaClass = jni::Class::Singleton(env); + auto field = javaClass.GetField(env, "fastPFOREnabled"); + return obj.Get(env, field); +} + } // namespace android } // namespace mbgl diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_options.hpp b/platform/android/MapLibreAndroid/src/cpp/native_map_options.hpp index 79ac13487..4f4c781fe 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_options.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_options.hpp @@ -20,6 +20,7 @@ public: static float pixelRatio(jni::JNIEnv&, const jni::Object&); static bool crossSourceCollisions(jni::JNIEnv&, const jni::Object&); static bool asyncRendererCleanup(jni::JNIEnv&, const jni::Object&); + static bool fastPFOREnabled(jni::JNIEnv&, const jni::Object&); }; } // namespace android diff --git a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp index e778f7032..f24ee5e0c 100644 --- a/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp @@ -87,7 +87,8 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env, .withPixelRatio(pixelRatio) .withConstrainMode(ConstrainMode::HeightOnly) .withViewportMode(ViewportMode::Default) - .withCrossSourceCollisions(NativeMapOptions::crossSourceCollisions(_env, jNativeMapOptions)); + .withCrossSourceCollisions(NativeMapOptions::crossSourceCollisions(_env, jNativeMapOptions)) + .withFastPFOREnabled(NativeMapOptions::fastPFOREnabled(_env, jNativeMapOptions)); // Create the core map map = std::make_unique( diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java index ebaf423e1..d249dd71e 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java @@ -102,6 +102,8 @@ public class MapLibreMapOptions implements Parcelable { private boolean asyncRendererCleanup = false; + private boolean fastPFOREnabled = false; + /** * Creates a new MapLibreMapOptions object. * @@ -167,6 +169,8 @@ public class MapLibreMapOptions implements Parcelable { actionJournalRenderingReportInterval = in.readInt(); asyncRendererCleanup = in.readByte() != 0; + + fastPFOREnabled = in.readByte() != 0; } /** @@ -340,6 +344,10 @@ public class MapLibreMapOptions implements Parcelable { maplibreMapOptions.asyncRendererCleanup( typedArray.getBoolean(R.styleable.maplibre_MapView_maplibre_asyncRendererCleanup, false) ); + + maplibreMapOptions.fastPFOREnabled( + typedArray.getBoolean(R.styleable.maplibre_MapView_maplibre_fastPFOREnabled, false) + ); } finally { typedArray.recycle(); } @@ -852,6 +860,16 @@ public class MapLibreMapOptions implements Parcelable { return this; } + /** + * Enable FastPFOR decompression for vector tiles, defaults to false. + * @param enable true to enable, false to disable + * @return This + */ + @NonNull public MapLibreMapOptions fastPFOREnabled(boolean enable) { + this.fastPFOREnabled = enable; + return this; + } + /** * Enable local ideograph font family, defaults to true. * @@ -999,6 +1017,15 @@ public class MapLibreMapOptions implements Parcelable { return asyncRendererCleanup; } + /** + * Check whether FastPFOR decompression is enabled for vector tiles. + * + * @return true if enabled + */ + public boolean getFastPFOREnabled() { + return fastPFOREnabled; + } + /** * Set the flag to render the map surface on top of another surface. * @@ -1391,6 +1418,8 @@ public class MapLibreMapOptions implements Parcelable { dest.writeInt(actionJournalRenderingReportInterval); dest.writeByte((byte) (asyncRendererCleanup ? 1 : 0)); + + dest.writeByte((byte) (fastPFOREnabled ? 1 : 0)); } @Override @@ -1535,6 +1564,10 @@ public class MapLibreMapOptions implements Parcelable { return false; } + if (fastPFOREnabled != options.fastPFOREnabled) { + return false; + } + return false; } @@ -1588,6 +1621,7 @@ public class MapLibreMapOptions implements Parcelable { result = 31 * result + (int) actionJournalLogFileCount; result = 31 * result + (int) actionJournalRenderingReportInterval; result = 31 * result + (asyncRendererCleanup ? 1 : 0); + result = 31 * result + (fastPFOREnabled ? 1 : 0); return result; } } diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapOptions.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapOptions.java index fbfb5f234..2ce57b494 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapOptions.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapOptions.java @@ -12,6 +12,8 @@ public class NativeMapOptions { private final int actionJournalRenderingReportInterval; private final boolean asyncRendererCleanup; + private final boolean fastPFOREnabled; + public NativeMapOptions(MapLibreMapOptions options) { pixelRatio = options.getPixelRatio(); crossSourceCollisions = options.getCrossSourceCollisions(); @@ -22,6 +24,7 @@ public class NativeMapOptions { actionJournalLogFileCount = options.getActionJournalLogFileCount(); actionJournalRenderingReportInterval = options.getActionJournalRenderingReportInterval(); asyncRendererCleanup = options.getAsyncRendererCleanup(); + fastPFOREnabled = options.getFastPFOREnabled(); } public NativeMapOptions(float pixelRatio, boolean crossSourceCollisions) { @@ -35,6 +38,7 @@ public class NativeMapOptions { actionJournalRenderingReportInterval = 0; asyncRendererCleanup = false; + fastPFOREnabled = false; } public float pixelRatio() { @@ -68,4 +72,8 @@ public class NativeMapOptions { public boolean asyncRendererCleanup() { return asyncRendererCleanup; } + + public boolean fastPFOREnabled() { + return fastPFOREnabled; + } } diff --git a/platform/android/MapLibreAndroid/src/main/res/values/attrs.xml b/platform/android/MapLibreAndroid/src/main/res/values/attrs.xml index eabee6de1..622e18640 100644 --- a/platform/android/MapLibreAndroid/src/main/res/values/attrs.xml +++ b/platform/android/MapLibreAndroid/src/main/res/values/attrs.xml @@ -11,6 +11,7 @@ + diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties index c61a118f7..8b0e74be9 100644 --- a/platform/android/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ +#Mon Jun 15 10:28:30 CDT 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/platform/android/settings.gradle.kts b/platform/android/settings.gradle.kts index 8c5b2212b..fa486c809 100644 --- a/platform/android/settings.gradle.kts +++ b/platform/android/settings.gradle.kts @@ -21,7 +21,7 @@ dependencyResolutionManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } include(":MapLibreAndroid", ":MapLibreAndroidTestApp", ":MapLibreAndroidLint") diff --git a/platform/darwin/src/MLNMapOptions.h b/platform/darwin/src/MLNMapOptions.h index 89ce52eb0..26c1cdbb5 100644 --- a/platform/darwin/src/MLNMapOptions.h +++ b/platform/darwin/src/MLNMapOptions.h @@ -37,6 +37,11 @@ MLN_EXPORT */ @property NSArray *pluginLayers; +/** + Enable FastPFOR decoding for vector tiles. Defaults to `NO`. + */ +@property bool fastPFOREnabled; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MLNMapOptions.mm b/platform/darwin/src/MLNMapOptions.mm index c91640917..b4f0aca7f 100644 --- a/platform/darwin/src/MLNMapOptions.mm +++ b/platform/darwin/src/MLNMapOptions.mm @@ -12,8 +12,8 @@ _styleURL = nil; _styleJSON = nil; _pluginLayers = nil; - _actionJournalOptions = [[MLNActionJournalOptions alloc] init]; + _fastPFOREnabled = NO; } return self; diff --git a/platform/ios/src/MLNMapView.mm b/platform/ios/src/MLNMapView.mm index 4744948dd..876b7388f 100644 --- a/platform/ios/src/MLNMapView.mm +++ b/platform/ios/src/MLNMapView.mm @@ -710,7 +710,8 @@ public: .withPixelRatio(config.scaleFactor) .withConstrainMode(mbgl::ConstrainMode::None) .withViewportMode(mbgl::ViewportMode::Default) - .withCrossSourceCollisions(enableCrossSourceCollisions); + .withCrossSourceCollisions(enableCrossSourceCollisions) + .withFastPFOREnabled(mlnMapoptions.fastPFOREnabled); mbgl::TileServerOptions *tileServerOptions = [[MLNSettings sharedSettings] tileServerOptionsInternal]; diff --git a/src/mbgl/map/map_impl.cpp b/src/mbgl/map/map_impl.cpp index 9e556a03c..e842aa142 100644 --- a/src/mbgl/map/map_impl.cpp +++ b/src/mbgl/map/map_impl.cpp @@ -54,6 +54,7 @@ Map::Impl::Impl(RendererFrontend& frontend_, mode(mapOptions.mapMode()), pixelRatio(mapOptions.pixelRatio()), crossSourceCollisions(mapOptions.crossSourceCollisions()), + fastPFOREnabled(mapOptions.fastPFOREnabled()), fileSource(std::move(fileSource_)), style(std::make_unique(fileSource, pixelRatio, frontend_.getThreadPool())), annotationManager(*style) { @@ -132,6 +133,7 @@ void Map::Impl::onUpdate() { .prefetchZoomDelta = prefetchZoomDelta, .stillImageRequest = bool(stillImageRequest), .crossSourceCollisions = crossSourceCollisions, + .fastPFOREnabled = fastPFOREnabled, .tileLodMinRadius = tileLodMinRadius, .tileLodScale = tileLodScale, .tileLodPitchThreshold = tileLodPitchThreshold, diff --git a/src/mbgl/map/map_impl.hpp b/src/mbgl/map/map_impl.hpp index 5ef9ffa43..fa25df9f0 100644 --- a/src/mbgl/map/map_impl.hpp +++ b/src/mbgl/map/map_impl.hpp @@ -91,6 +91,7 @@ public: const MapMode mode; const float pixelRatio; const bool crossSourceCollisions; + const bool fastPFOREnabled; MapDebugOptions debugOptions{MapDebugOptions::NoDebug}; std::unique_ptr renderingStatsView; diff --git a/src/mbgl/map/map_options.cpp b/src/mbgl/map/map_options.cpp index 656d09a65..e77ec0a16 100644 --- a/src/mbgl/map/map_options.cpp +++ b/src/mbgl/map/map_options.cpp @@ -11,6 +11,7 @@ public: bool crossSourceCollisions = true; Size size = {64, 64}; float pixelRatio = 1.0; + bool fastPFOREnabled = false; }; // These requires the complete type of Impl. @@ -82,4 +83,13 @@ float MapOptions::pixelRatio() const { return impl_->pixelRatio; } +MapOptions& MapOptions::withFastPFOREnabled(bool enable) { + impl_->fastPFOREnabled = enable; + return *this; +} + +bool MapOptions::fastPFOREnabled() const { + return impl_->fastPFOREnabled; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index 29ea0d746..8b5d45a28 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -339,6 +339,7 @@ std::unique_ptr RenderOrchestrator::createRenderTree( std::unique_ptr renderSource = RenderSource::create(entry.second, threadPool); renderSource->setObserver(this); renderSource->setCacheEnabled(tileCacheEnabled); + renderSource->setFastPFOREnabled(updateParameters->fastPFOREnabled); renderSources.emplace(entry.first, std::move(renderSource)); } transformState = updateParameters->transformState; diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 70f01e534..9f352d9ab 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -3,13 +3,14 @@ #include #include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include +#include #include #include @@ -21,28 +22,24 @@ namespace mbgl { using namespace style; std::unique_ptr RenderSource::create(const Immutable& impl, - const TaggedScheduler& threadPool_) { + const TaggedScheduler& threadPool) { switch (impl->type) { case SourceType::Vector: // the TileSet isn't available yet, so we can't make different sources by format - return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + return std::make_unique(staticImmutableCast(impl), threadPool); case SourceType::Raster: - return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + return std::make_unique(staticImmutableCast(impl), threadPool); case SourceType::RasterDEM: - return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + return std::make_unique(staticImmutableCast(impl), threadPool); case SourceType::GeoJSON: - return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + return std::make_unique(staticImmutableCast(impl), threadPool); case SourceType::Video: assert(false); return nullptr; case SourceType::Annotations: if (LayerManager::annotationsEnabled) { return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + threadPool); } else { assert(false); return nullptr; @@ -51,7 +48,7 @@ std::unique_ptr RenderSource::create(const Immutable return std::make_unique(staticImmutableCast(impl)); case SourceType::CustomVector: return std::make_unique(staticImmutableCast(impl), - std::move(threadPool_)); + threadPool); } // Not reachable, but placate GCC. diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index f54ace058..424161a43 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -33,6 +33,7 @@ class Tile; class TileParameters; class TransformParameters; class TransformState; +class UpdateParameters; namespace gfx { class UploadPass; @@ -103,7 +104,9 @@ public: const std::optional&, const std::optional&) {} - virtual void setCacheEnabled(bool) {}; + virtual void setCacheEnabled(bool) {} + + virtual void setFastPFOREnabled(bool) {} virtual void reduceMemoryUse() = 0; diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 3e90fd81a..c469c3e42 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -22,29 +22,30 @@ void RenderVectorSource::updateInternal(const Tileset& tileset, const bool needsRendering, const bool needsRelayout, const TileParameters& parameters) { - tilePyramid.update( - layers, - needsRendering, - needsRelayout, - parameters, - *baseImpl, - util::tileSize_I, - tileset.zoomRange, - tileset.bounds, - [&](const OverscaledTileID& tileID, TileObserver* observer_) -> std::unique_ptr { - if (!isMLT.has_value()) { - auto impl = staticImmutableCast(baseImpl); - assert(impl->tileset); // we should have one by now - if (impl->tileset) { - isMLT = (impl->tileset->vectorEncoding == Tileset::VectorEncoding::MLT); - } - } - if (isMLT && *isMLT) { - return std::make_unique(tileID, baseImpl->id, parameters, tileset, observer_); - } else { - return std::make_unique(tileID, baseImpl->id, parameters, tileset, observer_); - } - }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + *baseImpl, + util::tileSize_I, + tileset.zoomRange, + tileset.bounds, + [&](const OverscaledTileID& tileID, TileObserver* observer_) -> std::unique_ptr { + if (!isMLT.has_value()) { + auto impl = staticImmutableCast(baseImpl); + assert(impl->tileset); // we should have one by now + if (impl->tileset) { + isMLT = (impl->tileset->vectorEncoding == Tileset::VectorEncoding::MLT); + } + } + if (isMLT && *isMLT) { + return std::make_unique( + tileID, baseImpl->id, parameters, tileset, observer_, fastPFOREnabled); + } else { + return std::make_unique( + tileID, baseImpl->id, parameters, tileset, observer_); + } + }); } } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 602a2f9bb..f0dd6a49b 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -10,16 +10,22 @@ class RenderVectorSource final : public RenderTileSetSource { public: explicit RenderVectorSource(Immutable, const TaggedScheduler&); -private: + /// Enable the decoding of MLT tiles with FastPFOR integer encodings. + /// Default is false. Such tiles will fail if not explicitly enabled. + void setFastPFOREnabled(bool enable) override { fastPFOREnabled = enable; } + +protected: void updateInternal(const Tileset&, const std::vector>&, bool needsRendering, bool needsRelayout, const TileParameters&) override; + const std::optional& getTileset() const override; private: std::optional isMLT; + bool fastPFOREnabled = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp index 7e7520b4d..0c77e7d9c 100644 --- a/src/mbgl/renderer/update_parameters.hpp +++ b/src/mbgl/renderer/update_parameters.hpp @@ -48,6 +48,8 @@ public: const bool crossSourceCollisions; + const bool fastPFOREnabled = false; + double tileLodMinRadius = 3; double tileLodScale = 1; double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi; diff --git a/src/mbgl/tile/vector_mlt_tile.cpp b/src/mbgl/tile/vector_mlt_tile.cpp index 0df2136bb..fa138e6e4 100644 --- a/src/mbgl/tile/vector_mlt_tile.cpp +++ b/src/mbgl/tile/vector_mlt_tile.cpp @@ -12,8 +12,10 @@ VectorMLTTile::VectorMLTTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters_, const Tileset& tileset_, - TileObserver* observer_) - : VectorTile(id_, std::move(sourceID_), parameters_, tileset_, observer_) {} + TileObserver* observer_, + bool fastPFOREnabled_) + : VectorTile(id_, std::move(sourceID_), parameters_, tileset_, observer_), + fastPFOREnabled(fastPFOREnabled_) {} VectorMLTTile::~VectorMLTTile() { // this needs to be explicitly deleted in the most-derived destructor @@ -26,7 +28,7 @@ VectorMLTTile::~VectorMLTTile() { void VectorMLTTile::setData(const std::shared_ptr& data_) { if (!obsolete) { - GeometryTile::setData(data_ ? std::make_unique(data_) : nullptr); + GeometryTile::setData(data_ ? std::make_unique(data_, fastPFOREnabled) : nullptr); } } diff --git a/src/mbgl/tile/vector_mlt_tile.hpp b/src/mbgl/tile/vector_mlt_tile.hpp index e4a8fa9dd..40ec97155 100644 --- a/src/mbgl/tile/vector_mlt_tile.hpp +++ b/src/mbgl/tile/vector_mlt_tile.hpp @@ -15,11 +15,15 @@ public: std::string sourceID, const TileParameters&, const Tileset&, - TileObserver* observer = nullptr); + TileObserver* observer, + bool fastPFOREnabled); ~VectorMLTTile() override; void setData(const std::shared_ptr& data) override; + +private: + bool fastPFOREnabled; }; } // namespace mbgl diff --git a/src/mbgl/tile/vector_mlt_tile_data.cpp b/src/mbgl/tile/vector_mlt_tile_data.cpp index ac966460c..810dab969 100644 --- a/src/mbgl/tile/vector_mlt_tile_data.cpp +++ b/src/mbgl/tile/vector_mlt_tile_data.cpp @@ -78,7 +78,7 @@ const PropertyMap& VectorMLTTileFeature::getProperties() const { } FeatureIdentifier VectorMLTTileFeature::getID() const { - return feature.getID(); + return feature.getID().has_value() ? mapbox::feature::identifier(*feature.getID()) : mapbox::feature::null_value; } namespace { @@ -174,12 +174,14 @@ std::string VectorMLTTileLayer::getName() const { return layer.getName(); } -VectorMLTTileData::VectorMLTTileData(std::shared_ptr data_) - : data(std::move(data_)) {} +VectorMLTTileData::VectorMLTTileData(std::shared_ptr data_, bool fastPFOREnabled_) + : data(std::move(data_)), + fastPFOREnabled(fastPFOREnabled_) {} VectorMLTTileData::VectorMLTTileData(const VectorMLTTileData& other) : data(other.data), - tile(other.tile) {} + tile(other.tile), + fastPFOREnabled(other.fastPFOREnabled) {} std::unique_ptr VectorMLTTileData::clone() const { return std::make_unique(*this); @@ -191,7 +193,7 @@ std::unique_ptr VectorMLTTileData::getLayer(const std::string if (data && !tile) { try { mlt::DataView tileData{data->data(), data->size()}; - tile = std::make_shared(mlt::Decoder().decode(tileData)); + tile = std::make_shared(mlt::Decoder(fastPFOREnabled).decode(tileData)); } catch (const std::exception& ex) { Log::Warning(Event::ParseTile, "MLT parse failed: " + std::string(ex.what())); } diff --git a/src/mbgl/tile/vector_mlt_tile_data.hpp b/src/mbgl/tile/vector_mlt_tile_data.hpp index 5a7830250..35d9fb3b1 100644 --- a/src/mbgl/tile/vector_mlt_tile_data.hpp +++ b/src/mbgl/tile/vector_mlt_tile_data.hpp @@ -68,7 +68,7 @@ private: class VectorMLTTileData : public GeometryTileData { public: - VectorMLTTileData(std::shared_ptr data); + VectorMLTTileData(std::shared_ptr data, bool fastPFOREnabled); VectorMLTTileData(const VectorMLTTileData&); VectorMLTTileData(VectorMLTTileData&&) = default; @@ -80,6 +80,7 @@ public: private: mutable std::shared_ptr data; mutable std::shared_ptr tile; + bool fastPFOREnabled; }; } // namespace mbgl diff --git a/test/fixtures/map/issue12432/0-0-0-fastpfor.mlt b/test/fixtures/map/issue12432/0-0-0-fastpfor.mlt new file mode 100644 index 000000000..15ba1e413 Binary files /dev/null and b/test/fixtures/map/issue12432/0-0-0-fastpfor.mlt differ diff --git a/test/fixtures/map/issue12432/0-0-0.mlt b/test/fixtures/map/issue12432/0-0-0.mlt new file mode 100644 index 000000000..5b6d09e57 Binary files /dev/null and b/test/fixtures/map/issue12432/0-0-0.mlt differ diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 69e649a98..b4019193a 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -21,11 +21,12 @@ #include #include -#include -#include -#include #include +#include +#include +#include #include +#include #include #include @@ -46,10 +47,10 @@ #include #include -#include -#include #include +#include + using namespace mbgl; using SourceType = mbgl::style::SourceType; diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index 83e9dd2c9..7d917b5f5 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -20,6 +20,7 @@ #include #include +#include "mbgl/tile/vector_mlt_tile_data.hpp" using namespace mbgl; @@ -65,14 +66,7 @@ TEST(VectorTileData, ParseResults) { ASSERT_EQ(layer->getName(), "admin"); ASSERT_EQ(layer->featureCount(), 17154u); - try { - layer->getFeature(17154u); - ASSERT_TRUE(false) << "should throw: feature index is out of range."; - } catch (const std::out_of_range&) { - ASSERT_TRUE(true); - } catch (...) { // needed for iOS when MBGL_WITH_RTTI=OFF - ASSERT_TRUE(true); - } + ASSERT_THROW(layer->getFeature(17154u), std::out_of_range); std::unique_ptr feature = layer->getFeature(0u); ASSERT_EQ(feature->getType(), mbgl::FeatureType::LineString); @@ -85,3 +79,50 @@ TEST(VectorTileData, ParseResults) { ASSERT_EQ(feature->getValue("invalid"), std::nullopt); } + +TEST(VectorTileData, MLTParseResults) { + struct SubCase { + bool useFastPFOR; + bool fastPFOREnabled; + }; + for (const auto& testCase : std::vector{ + SubCase{.useFastPFOR = false, .fastPFOREnabled = false}, + SubCase{.useFastPFOR = false, .fastPFOREnabled = true}, + SubCase{.useFastPFOR = true, .fastPFOREnabled = true}, + SubCase{.useFastPFOR = true, .fastPFOREnabled = false}, + }) { + const auto path = "test/fixtures/map/issue12432/0-0-0" + + std::string(testCase.useFastPFOR ? "-fastpfor.mlt" : ".mlt"); + VectorMLTTileData data(std::make_shared(util::read_file(path)), testCase.fastPFOREnabled); + + std::vector layerNames = data.layerNames(); + + if (!testCase.fastPFOREnabled && testCase.useFastPFOR) { + // If fast PFOR is not enabled, the fast PFOR tile should fail to parse + ASSERT_EQ(layerNames.size(), 0u); + continue; + } + + // MLT layers are presented in the order they're encountered, not sorted alphabetically like MVT layers + ASSERT_EQ(layerNames.size(), 2u); + ASSERT_EQ(layerNames.at(0), "water"); + ASSERT_EQ(layerNames.at(1), "admin"); + + ASSERT_FALSE(data.getLayer("invalid")); + + std::unique_ptr layer = data.getLayer("admin"); + ASSERT_EQ(layer->getName(), "admin"); + ASSERT_EQ(layer->featureCount(), 17154u); + + std::unique_ptr feature = layer->getFeature(0u); + ASSERT_EQ(feature->getType(), mbgl::FeatureType::LineString); + ASSERT_TRUE(feature->getID().is()); + ASSERT_EQ(feature->getID().get(), 1u); + + const std::unordered_map& properties = feature->getProperties(); + ASSERT_EQ(properties.size(), 3u); + ASSERT_EQ(properties.at("disputed"), *feature->getValue("disputed")); + + ASSERT_EQ(feature->getValue("invalid"), std::nullopt); + } +} diff --git a/vendor/maplibre-tile-spec b/vendor/maplibre-tile-spec index f493beb62..d457bf2b0 160000 --- a/vendor/maplibre-tile-spec +++ b/vendor/maplibre-tile-spec @@ -1 +1 @@ -Subproject commit f493beb6272dafe172545ef314a3f044ee8c4dc1 +Subproject commit d457bf2b0135ecf718b9d8a1bea7e6fa5be2713e diff --git a/vendor/maplibre-tile-spec.cmake b/vendor/maplibre-tile-spec.cmake index 68a1ba71f..b653a351d 100644 --- a/vendor/maplibre-tile-spec.cmake +++ b/vendor/maplibre-tile-spec.cmake @@ -1,6 +1,5 @@ set(MLT_WITH_JSON OFF CACHE BOOL "No JSON support" FORCE) set(MLT_WITH_PROTOZERO OFF CACHE BOOL "No protozero" FORCE) -set(MLT_WITH_FASTPFOR OFF CACHE BOOL "disabled for lack of support for ARMv7 in SIMDE and requirement for SSE on x86" FORCE) set(MLT_WITH_TESTS OFF CACHE BOOL "Google Test conflicts with Dawn") set(MLT_WITH_TOOLS OFF CACHE BOOL "Not used") @@ -8,8 +7,4 @@ set(MLT_WITH_TOOLS OFF CACHE BOOL "Not used") set(CMAKE_OSX_DEPLOYMENT_TARGET 14.3) set(MLT_OSX_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET} CACHE STRING "Inherited MacOS/iOS target" FORCE) -if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.25") - add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp SYSTEM) -else() - add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp) -endif() +add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp SYSTEM)